• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 //#define DEBUG_UEVENTS
18 #define CHARGER_KLOG_LEVEL 6
19 
20 #include <dirent.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <linux/input.h>
24 #include <linux/netlink.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/poll.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sys/un.h>
34 #include <time.h>
35 #include <unistd.h>
36 
37 #include <cutils/android_reboot.h>
38 #include <cutils/klog.h>
39 #include <cutils/list.h>
40 #include <cutils/misc.h>
41 #include <cutils/uevent.h>
42 
43 #include "minui/minui.h"
44 
45 #ifndef max
46 #define max(a,b) ((a) > (b) ? (a) : (b))
47 #endif
48 
49 #ifndef min
50 #define min(a,b) ((a) < (b) ? (a) : (b))
51 #endif
52 
53 #define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
54 
55 #define MSEC_PER_SEC            (1000LL)
56 #define NSEC_PER_MSEC           (1000000LL)
57 
58 #define BATTERY_UNKNOWN_TIME    (2 * MSEC_PER_SEC)
59 #define POWER_ON_KEY_TIME       (2 * MSEC_PER_SEC)
60 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
61 
62 #define BATTERY_FULL_THRESH     95
63 
64 #define LAST_KMSG_PATH          "/proc/last_kmsg"
65 #define LAST_KMSG_MAX_SZ        (32 * 1024)
66 
67 #define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
68 #define LOGI(x...) do { KLOG_INFO("charger", x); } while (0)
69 #define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
70 
71 struct key_state {
72     bool pending;
73     bool down;
74     int64_t timestamp;
75 };
76 
77 struct power_supply {
78     struct listnode list;
79     char name[256];
80     char type[32];
81     bool online;
82     bool valid;
83     char cap_path[PATH_MAX];
84 };
85 
86 struct frame {
87     const char *name;
88     int disp_time;
89     int min_capacity;
90 
91     gr_surface surface;
92 };
93 
94 struct animation {
95     bool run;
96 
97     struct frame *frames;
98     int cur_frame;
99     int num_frames;
100 
101     int cur_cycle;
102     int num_cycles;
103 
104     /* current capacity being animated */
105     int capacity;
106 };
107 
108 struct charger {
109     int64_t next_screen_transition;
110     int64_t next_key_check;
111     int64_t next_pwr_check;
112 
113     struct key_state keys[KEY_MAX + 1];
114     int uevent_fd;
115 
116     struct listnode supplies;
117     int num_supplies;
118     int num_supplies_online;
119 
120     struct animation *batt_anim;
121     gr_surface surf_unknown;
122 
123     struct power_supply *battery;
124 };
125 
126 struct uevent {
127     const char *action;
128     const char *path;
129     const char *subsystem;
130     const char *ps_name;
131     const char *ps_type;
132     const char *ps_online;
133 };
134 
135 static struct frame batt_anim_frames[] = {
136     {
137         .name = "charger/battery_0",
138         .disp_time = 750,
139         .min_capacity = 0,
140     },
141     {
142         .name = "charger/battery_1",
143         .disp_time = 750,
144         .min_capacity = 20,
145     },
146     {
147         .name = "charger/battery_2",
148         .disp_time = 750,
149         .min_capacity = 40,
150     },
151     {
152         .name = "charger/battery_3",
153         .disp_time = 750,
154         .min_capacity = 60,
155     },
156     {
157         .name = "charger/battery_4",
158         .disp_time = 750,
159         .min_capacity = 80,
160     },
161     {
162         .name = "charger/battery_5",
163         .disp_time = 750,
164         .min_capacity = BATTERY_FULL_THRESH,
165     },
166 };
167 
168 static struct animation battery_animation = {
169     .frames = batt_anim_frames,
170     .num_frames = ARRAY_SIZE(batt_anim_frames),
171     .num_cycles = 3,
172 };
173 
174 static struct charger charger_state = {
175     .batt_anim = &battery_animation,
176 };
177 
178 static int char_width;
179 static int char_height;
180 
181 /* current time in milliseconds */
curr_time_ms(void)182 static int64_t curr_time_ms(void)
183 {
184     struct timespec tm;
185     clock_gettime(CLOCK_MONOTONIC, &tm);
186     return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
187 }
188 
clear_screen(void)189 static void clear_screen(void)
190 {
191     gr_color(0, 0, 0, 255);
192     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
193 };
194 
195 #define MAX_KLOG_WRITE_BUF_SZ 256
196 
dump_last_kmsg(void)197 static void dump_last_kmsg(void)
198 {
199     char *buf;
200     char *ptr;
201     unsigned sz = 0;
202     int len;
203 
204     LOGI("\n");
205     LOGI("*************** LAST KMSG ***************\n");
206     LOGI("\n");
207     buf = load_file(LAST_KMSG_PATH, &sz);
208     if (!buf || !sz) {
209         LOGI("last_kmsg not found. Cold reset?\n");
210         goto out;
211     }
212 
213     len = min(sz, LAST_KMSG_MAX_SZ);
214     ptr = buf + (sz - len);
215 
216     while (len > 0) {
217         int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
218         char yoink;
219         char *nl;
220 
221         nl = memrchr(ptr, '\n', cnt - 1);
222         if (nl)
223             cnt = nl - ptr + 1;
224 
225         yoink = ptr[cnt];
226         ptr[cnt] = '\0';
227         klog_write(6, "<6>%s", ptr);
228         ptr[cnt] = yoink;
229 
230         len -= cnt;
231         ptr += cnt;
232     }
233 
234     free(buf);
235 
236 out:
237     LOGI("\n");
238     LOGI("************* END LAST KMSG *************\n");
239     LOGI("\n");
240 }
241 
read_file(const char * path,char * buf,size_t sz)242 static int read_file(const char *path, char *buf, size_t sz)
243 {
244     int fd;
245     size_t cnt;
246 
247     fd = open(path, O_RDONLY, 0);
248     if (fd < 0)
249         goto err;
250 
251     cnt = read(fd, buf, sz - 1);
252     if (cnt <= 0)
253         goto err;
254     buf[cnt] = '\0';
255     if (buf[cnt - 1] == '\n') {
256         cnt--;
257         buf[cnt] = '\0';
258     }
259 
260     close(fd);
261     return cnt;
262 
263 err:
264     if (fd >= 0)
265         close(fd);
266     return -1;
267 }
268 
read_file_int(const char * path,int * val)269 static int read_file_int(const char *path, int *val)
270 {
271     char buf[32];
272     int ret;
273     int tmp;
274     char *end;
275 
276     ret = read_file(path, buf, sizeof(buf));
277     if (ret < 0)
278         return -1;
279 
280     tmp = strtol(buf, &end, 0);
281     if (end == buf ||
282         ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0')))
283         goto err;
284 
285     *val = tmp;
286     return 0;
287 
288 err:
289     return -1;
290 }
291 
get_battery_capacity(struct charger * charger)292 static int get_battery_capacity(struct charger *charger)
293 {
294     int ret;
295     int batt_cap = -1;
296 
297     if (!charger->battery)
298         return -1;
299 
300     ret = read_file_int(charger->battery->cap_path, &batt_cap);
301     if (ret < 0 || batt_cap > 100) {
302         batt_cap = -1;
303     }
304 
305     return batt_cap;
306 }
307 
find_supply(struct charger * charger,const char * name)308 static struct power_supply *find_supply(struct charger *charger,
309                                         const char *name)
310 {
311     struct listnode *node;
312     struct power_supply *supply;
313 
314     list_for_each(node, &charger->supplies) {
315         supply = node_to_item(node, struct power_supply, list);
316         if (!strncmp(name, supply->name, sizeof(supply->name)))
317             return supply;
318     }
319     return NULL;
320 }
321 
add_supply(struct charger * charger,const char * name,const char * type,const char * path,bool online)322 static struct power_supply *add_supply(struct charger *charger,
323                                        const char *name, const char *type,
324                                        const char *path, bool online)
325 {
326     struct power_supply *supply;
327 
328     supply = calloc(1, sizeof(struct power_supply));
329     if (!supply)
330         return NULL;
331 
332     strlcpy(supply->name, name, sizeof(supply->name));
333     strlcpy(supply->type, type, sizeof(supply->type));
334     snprintf(supply->cap_path, sizeof(supply->cap_path),
335              "/sys/%s/capacity", path);
336     supply->online = online;
337     list_add_tail(&charger->supplies, &supply->list);
338     charger->num_supplies++;
339     LOGV("... added %s %s %d\n", supply->name, supply->type, online);
340     return supply;
341 }
342 
remove_supply(struct charger * charger,struct power_supply * supply)343 static void remove_supply(struct charger *charger, struct power_supply *supply)
344 {
345     if (!supply)
346         return;
347     list_remove(&supply->list);
348     charger->num_supplies--;
349     free(supply);
350 }
351 
parse_uevent(const char * msg,struct uevent * uevent)352 static void parse_uevent(const char *msg, struct uevent *uevent)
353 {
354     uevent->action = "";
355     uevent->path = "";
356     uevent->subsystem = "";
357     uevent->ps_name = "";
358     uevent->ps_online = "";
359     uevent->ps_type = "";
360 
361     /* currently ignoring SEQNUM */
362     while (*msg) {
363 #ifdef DEBUG_UEVENTS
364         LOGV("uevent str: %s\n", msg);
365 #endif
366         if (!strncmp(msg, "ACTION=", 7)) {
367             msg += 7;
368             uevent->action = msg;
369         } else if (!strncmp(msg, "DEVPATH=", 8)) {
370             msg += 8;
371             uevent->path = msg;
372         } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
373             msg += 10;
374             uevent->subsystem = msg;
375         } else if (!strncmp(msg, "POWER_SUPPLY_NAME=", 18)) {
376             msg += 18;
377             uevent->ps_name = msg;
378         } else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) {
379             msg += 20;
380             uevent->ps_online = msg;
381         } else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) {
382             msg += 18;
383             uevent->ps_type = msg;
384         }
385 
386         /* advance to after the next \0 */
387         while (*msg++)
388             ;
389     }
390 
391     LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n",
392          uevent->action, uevent->path, uevent->subsystem,
393          uevent->ps_name, uevent->ps_type, uevent->ps_online);
394 }
395 
process_ps_uevent(struct charger * charger,struct uevent * uevent)396 static void process_ps_uevent(struct charger *charger, struct uevent *uevent)
397 {
398     int online;
399     char ps_type[32];
400     struct power_supply *supply = NULL;
401     int i;
402     bool was_online = false;
403     bool battery = false;
404 
405     if (uevent->ps_type[0] == '\0') {
406         char *path;
407         int ret;
408 
409         if (uevent->path[0] == '\0')
410             return;
411         ret = asprintf(&path, "/sys/%s/type", uevent->path);
412         if (ret <= 0)
413             return;
414         ret = read_file(path, ps_type, sizeof(ps_type));
415         free(path);
416         if (ret < 0)
417             return;
418     } else {
419         strlcpy(ps_type, uevent->ps_type, sizeof(ps_type));
420     }
421 
422     if (!strncmp(ps_type, "Battery", 7))
423         battery = true;
424 
425     online = atoi(uevent->ps_online);
426     supply = find_supply(charger, uevent->ps_name);
427     if (supply) {
428         was_online = supply->online;
429         supply->online = online;
430     }
431 
432     if (!strcmp(uevent->action, "add")) {
433         if (!supply) {
434             supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path,
435                                 online);
436             if (!supply) {
437                 LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name,
438                      uevent->ps_type, online);
439                 return;
440             }
441             /* only pick up the first battery for now */
442             if (battery && !charger->battery)
443                 charger->battery = supply;
444         } else {
445             LOGE("supply '%s' already exists..\n", uevent->ps_name);
446         }
447     } else if (!strcmp(uevent->action, "remove")) {
448         if (supply) {
449             if (charger->battery == supply)
450                 charger->battery = NULL;
451             remove_supply(charger, supply);
452             supply = NULL;
453         }
454     } else if (!strcmp(uevent->action, "change")) {
455         if (!supply) {
456             LOGE("power supply '%s' not found ('%s' %d)\n",
457                  uevent->ps_name, ps_type, online);
458             return;
459         }
460     } else {
461         return;
462     }
463 
464     /* allow battery to be managed in the supply list but make it not
465      * contribute to online power supplies. */
466     if (!battery) {
467         if (was_online && !online)
468             charger->num_supplies_online--;
469         else if (supply && !was_online && online)
470             charger->num_supplies_online++;
471     }
472 
473     LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n",
474          uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline",
475          uevent->action, charger->num_supplies_online, charger->num_supplies);
476 }
477 
process_uevent(struct charger * charger,struct uevent * uevent)478 static void process_uevent(struct charger *charger, struct uevent *uevent)
479 {
480     if (!strcmp(uevent->subsystem, "power_supply"))
481         process_ps_uevent(charger, uevent);
482 }
483 
484 #define UEVENT_MSG_LEN  1024
handle_uevent_fd(struct charger * charger,int fd)485 static int handle_uevent_fd(struct charger *charger, int fd)
486 {
487     char msg[UEVENT_MSG_LEN+2];
488     int n;
489 
490     if (fd < 0)
491         return -1;
492 
493     while (true) {
494         struct uevent uevent;
495 
496         n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN);
497         if (n <= 0)
498             break;
499         if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
500             continue;
501 
502         msg[n] = '\0';
503         msg[n+1] = '\0';
504 
505         parse_uevent(msg, &uevent);
506         process_uevent(charger, &uevent);
507     }
508 
509     return 0;
510 }
511 
uevent_callback(int fd,short revents,void * data)512 static int uevent_callback(int fd, short revents, void *data)
513 {
514     struct charger *charger = data;
515 
516     if (!(revents & POLLIN))
517         return -1;
518     return handle_uevent_fd(charger, fd);
519 }
520 
521 /* force the kernel to regenerate the change events for the existing
522  * devices, if valid */
do_coldboot(struct charger * charger,DIR * d,const char * event,bool follow_links,int max_depth)523 static void do_coldboot(struct charger *charger, DIR *d, const char *event,
524                         bool follow_links, int max_depth)
525 {
526     struct dirent *de;
527     int dfd, fd;
528 
529     dfd = dirfd(d);
530 
531     fd = openat(dfd, "uevent", O_WRONLY);
532     if (fd >= 0) {
533         write(fd, event, strlen(event));
534         close(fd);
535         handle_uevent_fd(charger, charger->uevent_fd);
536     }
537 
538     while ((de = readdir(d)) && max_depth > 0) {
539         DIR *d2;
540 
541         LOGV("looking at '%s'\n", de->d_name);
542 
543         if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) ||
544            de->d_name[0] == '.') {
545             LOGV("skipping '%s' type %d (depth=%d follow=%d)\n",
546                  de->d_name, de->d_type, max_depth, follow_links);
547             continue;
548         }
549         LOGV("can descend into '%s'\n", de->d_name);
550 
551         fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
552         if (fd < 0) {
553             LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name,
554                  errno, strerror(errno));
555             continue;
556         }
557 
558         d2 = fdopendir(fd);
559         if (d2 == 0)
560             close(fd);
561         else {
562             LOGV("opened '%s'\n", de->d_name);
563             do_coldboot(charger, d2, event, follow_links, max_depth - 1);
564             closedir(d2);
565         }
566     }
567 }
568 
coldboot(struct charger * charger,const char * path,const char * event)569 static void coldboot(struct charger *charger, const char *path,
570                      const char *event)
571 {
572     char str[256];
573 
574     LOGV("doing coldboot '%s' in '%s'\n", event, path);
575     DIR *d = opendir(path);
576     if (d) {
577         snprintf(str, sizeof(str), "%s\n", event);
578         do_coldboot(charger, d, str, true, 1);
579         closedir(d);
580     }
581 }
582 
draw_text(const char * str,int x,int y)583 static int draw_text(const char *str, int x, int y)
584 {
585     int str_len_px = gr_measure(str);
586 
587     if (x < 0)
588         x = (gr_fb_width() - str_len_px) / 2;
589     if (y < 0)
590         y = (gr_fb_height() - char_height) / 2;
591     gr_text(x, y, str);
592 
593     return y + char_height;
594 }
595 
android_green(void)596 static void android_green(void)
597 {
598     gr_color(0xa4, 0xc6, 0x39, 255);
599 }
600 
601 /* returns the last y-offset of where the surface ends */
draw_surface_centered(struct charger * charger,gr_surface surface)602 static int draw_surface_centered(struct charger *charger, gr_surface surface)
603 {
604     int w;
605     int h;
606     int x;
607     int y;
608 
609     w = gr_get_width(surface);
610     h = gr_get_height(surface);
611     x = (gr_fb_width() - w) / 2 ;
612     y = (gr_fb_height() - h) / 2 ;
613 
614     LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
615     gr_blit(surface, 0, 0, w, h, x, y);
616     return y + h;
617 }
618 
draw_unknown(struct charger * charger)619 static void draw_unknown(struct charger *charger)
620 {
621     int y;
622     if (charger->surf_unknown) {
623         draw_surface_centered(charger, charger->surf_unknown);
624     } else {
625         android_green();
626         y = draw_text("Charging!", -1, -1);
627         draw_text("?\?/100", -1, y + 25);
628     }
629 }
630 
draw_battery(struct charger * charger)631 static void draw_battery(struct charger *charger)
632 {
633     struct animation *batt_anim = charger->batt_anim;
634     struct frame *frame = &batt_anim->frames[batt_anim->cur_frame];
635 
636     if (batt_anim->num_frames != 0) {
637         draw_surface_centered(charger, frame->surface);
638         LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n",
639              batt_anim->cur_frame, frame->name, frame->min_capacity,
640              frame->disp_time);
641     }
642 }
643 
redraw_screen(struct charger * charger)644 static void redraw_screen(struct charger *charger)
645 {
646     struct animation *batt_anim = charger->batt_anim;
647 
648     clear_screen();
649 
650     /* try to display *something* */
651     if (batt_anim->capacity < 0 || batt_anim->num_frames == 0)
652         draw_unknown(charger);
653     else
654         draw_battery(charger);
655     gr_flip();
656 }
657 
kick_animation(struct animation * anim)658 static void kick_animation(struct animation *anim)
659 {
660     anim->run = true;
661 }
662 
reset_animation(struct animation * anim)663 static void reset_animation(struct animation *anim)
664 {
665     anim->cur_cycle = 0;
666     anim->cur_frame = 0;
667     anim->run = false;
668 }
669 
update_screen_state(struct charger * charger,int64_t now)670 static void update_screen_state(struct charger *charger, int64_t now)
671 {
672     struct animation *batt_anim = charger->batt_anim;
673     int cur_frame;
674     int disp_time;
675 
676     if (!batt_anim->run || now < charger->next_screen_transition)
677         return;
678 
679     /* animation is over, blank screen and leave */
680     if (batt_anim->cur_cycle == batt_anim->num_cycles) {
681         reset_animation(batt_anim);
682         charger->next_screen_transition = -1;
683         gr_fb_blank(true);
684         LOGV("[%lld] animation done\n", now);
685         return;
686     }
687 
688     disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
689 
690     /* animation starting, set up the animation */
691     if (batt_anim->cur_frame == 0) {
692         int batt_cap;
693         int ret;
694 
695         LOGV("[%lld] animation starting\n", now);
696         batt_cap = get_battery_capacity(charger);
697         if (batt_cap >= 0 && batt_anim->num_frames != 0) {
698             int i;
699 
700             /* find first frame given current capacity */
701             for (i = 1; i < batt_anim->num_frames; i++) {
702                 if (batt_cap < batt_anim->frames[i].min_capacity)
703                     break;
704             }
705             batt_anim->cur_frame = i - 1;
706 
707             /* show the first frame for twice as long */
708             disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
709         }
710 
711         batt_anim->capacity = batt_cap;
712     }
713 
714     /* unblank the screen  on first cycle */
715     if (batt_anim->cur_cycle == 0)
716         gr_fb_blank(false);
717 
718     /* draw the new frame (@ cur_frame) */
719     redraw_screen(charger);
720 
721     /* if we don't have anim frames, we only have one image, so just bump
722      * the cycle counter and exit
723      */
724     if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
725         LOGV("[%lld] animation missing or unknown battery status\n", now);
726         charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
727         batt_anim->cur_cycle++;
728         return;
729     }
730 
731     /* schedule next screen transition */
732     charger->next_screen_transition = now + disp_time;
733 
734     /* advance frame cntr to the next valid frame
735      * if necessary, advance cycle cntr, and reset frame cntr
736      */
737     batt_anim->cur_frame++;
738     if (batt_anim->cur_frame == batt_anim->num_frames) {
739         batt_anim->cur_cycle++;
740         batt_anim->cur_frame = 0;
741 
742         /* don't reset the cycle counter, since we use that as a signal
743          * in a test above to check if animation is over
744          */
745     }
746 }
747 
set_key_callback(int code,int value,void * data)748 static int set_key_callback(int code, int value, void *data)
749 {
750     struct charger *charger = data;
751     int64_t now = curr_time_ms();
752     int down = !!value;
753 
754     if (code > KEY_MAX)
755         return -1;
756 
757     /* ignore events that don't modify our state */
758     if (charger->keys[code].down == down)
759         return 0;
760 
761     /* only record the down even timestamp, as the amount
762      * of time the key spent not being pressed is not useful */
763     if (down)
764         charger->keys[code].timestamp = now;
765     charger->keys[code].down = down;
766     charger->keys[code].pending = true;
767     if (down) {
768         LOGV("[%lld] key[%d] down\n", now, code);
769     } else {
770         int64_t duration = now - charger->keys[code].timestamp;
771         int64_t secs = duration / 1000;
772         int64_t msecs = duration - secs * 1000;
773         LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now,
774             code, secs, msecs);
775     }
776 
777     return 0;
778 }
779 
update_input_state(struct charger * charger,struct input_event * ev)780 static void update_input_state(struct charger *charger,
781                                struct input_event *ev)
782 {
783     if (ev->type != EV_KEY)
784         return;
785     set_key_callback(ev->code, ev->value, charger);
786 }
787 
set_next_key_check(struct charger * charger,struct key_state * key,int64_t timeout)788 static void set_next_key_check(struct charger *charger,
789                                struct key_state *key,
790                                int64_t timeout)
791 {
792     int64_t then = key->timestamp + timeout;
793 
794     if (charger->next_key_check == -1 || then < charger->next_key_check)
795         charger->next_key_check = then;
796 }
797 
process_key(struct charger * charger,int code,int64_t now)798 static void process_key(struct charger *charger, int code, int64_t now)
799 {
800     struct key_state *key = &charger->keys[code];
801     int64_t next_key_check;
802 
803     if (code == KEY_POWER) {
804         if (key->down) {
805             int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
806             if (now >= reboot_timeout) {
807                 LOGI("[%lld] rebooting\n", now);
808                 android_reboot(ANDROID_RB_RESTART, 0, 0);
809             } else {
810                 /* if the key is pressed but timeout hasn't expired,
811                  * make sure we wake up at the right-ish time to check
812                  */
813                 set_next_key_check(charger, key, POWER_ON_KEY_TIME);
814             }
815         } else {
816             /* if the power key got released, force screen state cycle */
817             if (key->pending)
818                 kick_animation(charger->batt_anim);
819         }
820     }
821 
822     key->pending = false;
823 }
824 
handle_input_state(struct charger * charger,int64_t now)825 static void handle_input_state(struct charger *charger, int64_t now)
826 {
827     process_key(charger, KEY_POWER, now);
828 
829     if (charger->next_key_check != -1 && now > charger->next_key_check)
830         charger->next_key_check = -1;
831 }
832 
handle_power_supply_state(struct charger * charger,int64_t now)833 static void handle_power_supply_state(struct charger *charger, int64_t now)
834 {
835     if (charger->num_supplies_online == 0) {
836         if (charger->next_pwr_check == -1) {
837             charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
838             LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
839                  now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
840         } else if (now >= charger->next_pwr_check) {
841             LOGI("[%lld] shutting down\n", now);
842             android_reboot(ANDROID_RB_POWEROFF, 0, 0);
843         } else {
844             /* otherwise we already have a shutdown timer scheduled */
845         }
846     } else {
847         /* online supply present, reset shutdown timer if set */
848         if (charger->next_pwr_check != -1) {
849             LOGI("[%lld] device plugged in: shutdown cancelled\n", now);
850             kick_animation(charger->batt_anim);
851         }
852         charger->next_pwr_check = -1;
853     }
854 }
855 
wait_next_event(struct charger * charger,int64_t now)856 static void wait_next_event(struct charger *charger, int64_t now)
857 {
858     int64_t next_event = INT64_MAX;
859     int64_t timeout;
860     struct input_event ev;
861     int ret;
862 
863     LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now,
864          charger->next_screen_transition, charger->next_key_check,
865          charger->next_pwr_check);
866 
867     if (charger->next_screen_transition != -1)
868         next_event = charger->next_screen_transition;
869     if (charger->next_key_check != -1 && charger->next_key_check < next_event)
870         next_event = charger->next_key_check;
871     if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
872         next_event = charger->next_pwr_check;
873 
874     if (next_event != -1 && next_event != INT64_MAX)
875         timeout = max(0, next_event - now);
876     else
877         timeout = -1;
878     LOGV("[%lld] blocking (%lld)\n", now, timeout);
879     ret = ev_wait((int)timeout);
880     if (!ret)
881         ev_dispatch();
882 }
883 
input_callback(int fd,short revents,void * data)884 static int input_callback(int fd, short revents, void *data)
885 {
886     struct charger *charger = data;
887     struct input_event ev;
888     int ret;
889 
890     ret = ev_get_input(fd, revents, &ev);
891     if (ret)
892         return -1;
893     update_input_state(charger, &ev);
894     return 0;
895 }
896 
event_loop(struct charger * charger)897 static void event_loop(struct charger *charger)
898 {
899     int ret;
900 
901     while (true) {
902         int64_t now = curr_time_ms();
903 
904         LOGV("[%lld] event_loop()\n", now);
905         handle_input_state(charger, now);
906         handle_power_supply_state(charger, now);
907 
908         /* do screen update last in case any of the above want to start
909          * screen transitions (animations, etc)
910          */
911         update_screen_state(charger, now);
912 
913         wait_next_event(charger, now);
914     }
915 }
916 
main(int argc,char ** argv)917 int main(int argc, char **argv)
918 {
919     int ret;
920     struct charger *charger = &charger_state;
921     int64_t now = curr_time_ms() - 1;
922     int fd;
923     int i;
924 
925     list_init(&charger->supplies);
926 
927     klog_init();
928     klog_set_level(CHARGER_KLOG_LEVEL);
929 
930     dump_last_kmsg();
931 
932     LOGI("--------------- STARTING CHARGER MODE ---------------\n");
933 
934     gr_init();
935     gr_font_size(&char_width, &char_height);
936 
937     ev_init(input_callback, charger);
938 
939     fd = uevent_open_socket(64*1024, true);
940     if (fd >= 0) {
941         fcntl(fd, F_SETFL, O_NONBLOCK);
942         ev_add_fd(fd, uevent_callback, charger);
943     }
944     charger->uevent_fd = fd;
945     coldboot(charger, "/sys/class/power_supply", "add");
946 
947     ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
948     if (ret < 0) {
949         LOGE("Cannot load image\n");
950         charger->surf_unknown = NULL;
951     }
952 
953     for (i = 0; i < charger->batt_anim->num_frames; i++) {
954         struct frame *frame = &charger->batt_anim->frames[i];
955 
956         ret = res_create_surface(frame->name, &frame->surface);
957         if (ret < 0) {
958             LOGE("Cannot load image %s\n", frame->name);
959             /* TODO: free the already allocated surfaces... */
960             charger->batt_anim->num_frames = 0;
961             charger->batt_anim->num_cycles = 1;
962             break;
963         }
964     }
965 
966     ev_sync_key_state(set_key_callback, charger);
967 
968     gr_fb_blank(true);
969 
970     charger->next_screen_transition = now - 1;
971     charger->next_key_check = -1;
972     charger->next_pwr_check = -1;
973     reset_animation(charger->batt_anim);
974     kick_animation(charger->batt_anim);
975 
976     event_loop(charger);
977 
978     return 0;
979 }
980