healthd_mode_charger.cpp revision 565ba02b89b64deb8bf7232ac2c2a38b01f63523
1/* 2 * Copyright (C) 2011-2013 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 <linux/input.h> 22#include <stdbool.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <sys/epoll.h> 27#include <sys/stat.h> 28#include <sys/types.h> 29#include <sys/un.h> 30#include <time.h> 31#include <unistd.h> 32 33#include <android-base/file.h> 34#include <android-base/stringprintf.h> 35 36#include <sys/socket.h> 37#include <linux/netlink.h> 38 39#include <batteryservice/BatteryService.h> 40#include <cutils/android_reboot.h> 41#include <cutils/klog.h> 42#include <cutils/misc.h> 43#include <cutils/uevent.h> 44#include <cutils/properties.h> 45 46#ifdef CHARGER_ENABLE_SUSPEND 47#include <suspend/autosuspend.h> 48#endif 49 50#include "animation.h" 51#include "AnimationParser.h" 52#include "minui/minui.h" 53 54#include <healthd/healthd.h> 55 56using namespace android; 57 58char *locale; 59 60#ifndef max 61#define max(a,b) ((a) > (b) ? (a) : (b)) 62#endif 63 64#ifndef min 65#define min(a,b) ((a) < (b) ? (a) : (b)) 66#endif 67 68#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 69 70#define MSEC_PER_SEC (1000LL) 71#define NSEC_PER_MSEC (1000000LL) 72 73#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC) 74#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) 75#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) 76 77#define LAST_KMSG_PATH "/proc/last_kmsg" 78#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops" 79#define LAST_KMSG_MAX_SZ (32 * 1024) 80 81#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) 82#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0) 83#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) 84 85static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt"; 86 87struct key_state { 88 bool pending; 89 bool down; 90 int64_t timestamp; 91}; 92 93struct charger { 94 bool have_battery_state; 95 bool charger_connected; 96 int64_t next_screen_transition; 97 int64_t next_key_check; 98 int64_t next_pwr_check; 99 100 struct key_state keys[KEY_MAX + 1]; 101 102 struct animation *batt_anim; 103 GRSurface* surf_unknown; 104 int boot_min_cap; 105}; 106 107static const struct animation BASE_ANIMATION = { 108 .text_clock = { 109 .pos_x = 0, 110 .pos_y = 0, 111 112 .color_r = 255, 113 .color_g = 255, 114 .color_b = 255, 115 .color_a = 255, 116 117 .font = nullptr, 118 }, 119 .text_percent = { 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 144static struct animation::frame default_animation_frames[] = { 145 { 146 .disp_time = 750, 147 .min_level = 0, 148 .max_level = 19, 149 .surface = NULL, 150 }, 151 { 152 .disp_time = 750, 153 .min_level = 0, 154 .max_level = 39, 155 .surface = NULL, 156 }, 157 { 158 .disp_time = 750, 159 .min_level = 0, 160 .max_level = 59, 161 .surface = NULL, 162 }, 163 { 164 .disp_time = 750, 165 .min_level = 0, 166 .max_level = 79, 167 .surface = NULL, 168 }, 169 { 170 .disp_time = 750, 171 .min_level = 80, 172 .max_level = 95, 173 .surface = NULL, 174 }, 175 { 176 .disp_time = 750, 177 .min_level = 0, 178 .max_level = 100, 179 .surface = NULL, 180 }, 181}; 182 183static struct animation battery_animation = BASE_ANIMATION; 184 185static struct charger charger_state; 186static struct healthd_config *healthd_config; 187static struct android::BatteryProperties *batt_prop; 188static int char_width; 189static int char_height; 190static bool minui_inited; 191 192/* current time in milliseconds */ 193static int64_t curr_time_ms(void) 194{ 195 struct timespec tm; 196 clock_gettime(CLOCK_MONOTONIC, &tm); 197 return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC); 198} 199 200static void clear_screen(void) 201{ 202 gr_color(0, 0, 0, 255); 203 gr_clear(); 204} 205 206#define MAX_KLOG_WRITE_BUF_SZ 256 207 208static void dump_last_kmsg(void) 209{ 210 char *buf; 211 char *ptr; 212 unsigned sz = 0; 213 int len; 214 215 LOGW("\n"); 216 LOGW("*************** LAST KMSG ***************\n"); 217 LOGW("\n"); 218 buf = (char *)load_file(LAST_KMSG_PSTORE_PATH, &sz); 219 220 if (!buf || !sz) { 221 buf = (char *)load_file(LAST_KMSG_PATH, &sz); 222 if (!buf || !sz) { 223 LOGW("last_kmsg not found. Cold reset?\n"); 224 goto out; 225 } 226 } 227 228 len = min(sz, LAST_KMSG_MAX_SZ); 229 ptr = buf + (sz - len); 230 231 while (len > 0) { 232 int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ); 233 char yoink; 234 char *nl; 235 236 nl = (char *)memrchr(ptr, '\n', cnt - 1); 237 if (nl) 238 cnt = nl - ptr + 1; 239 240 yoink = ptr[cnt]; 241 ptr[cnt] = '\0'; 242 klog_write(6, "<4>%s", ptr); 243 ptr[cnt] = yoink; 244 245 len -= cnt; 246 ptr += cnt; 247 } 248 249 free(buf); 250 251out: 252 LOGW("\n"); 253 LOGW("************* END LAST KMSG *************\n"); 254 LOGW("\n"); 255} 256 257#ifdef CHARGER_ENABLE_SUSPEND 258static int request_suspend(bool enable) 259{ 260 if (enable) 261 return autosuspend_enable(); 262 else 263 return autosuspend_disable(); 264} 265#else 266static int request_suspend(bool /*enable*/) 267{ 268 return 0; 269} 270#endif 271 272static int draw_text(const char *str, int x, int y) 273{ 274 int str_len_px = gr_measure(gr_sys_font(), str); 275 276 if (x < 0) 277 x = (gr_fb_width() - str_len_px) / 2; 278 if (y < 0) 279 y = (gr_fb_height() - char_height) / 2; 280 gr_text(gr_sys_font(), x, y, str, 0); 281 282 return y + char_height; 283} 284 285static void android_green(void) 286{ 287 gr_color(0xa4, 0xc6, 0x39, 255); 288} 289 290// Negative x or y coordinates position the text away from the opposite edge that positive ones do. 291void determine_xy(const animation::text_field& field, const int length, int* x, int* y) 292{ 293 *x = field.pos_x; 294 *y = field.pos_y; 295 296 int str_len_px = length * field.font->char_width; 297 if (field.pos_x == CENTER_VAL) { 298 *x = (gr_fb_width() - str_len_px) / 2; 299 } else if (field.pos_x >= 0) { 300 *x = field.pos_x; 301 } else { // position from max edge 302 *x = gr_fb_width() + field.pos_x - str_len_px; 303 } 304 305 if (field.pos_y == CENTER_VAL) { 306 *y = (gr_fb_height() - field.font->char_height) / 2; 307 } else if (field.pos_y >= 0) { 308 *y = field.pos_y; 309 } else { // position from max edge 310 *y = gr_fb_height() + field.pos_y - field.font->char_height; 311 } 312} 313 314static void draw_clock(const animation& anim) 315{ 316 static constexpr char CLOCK_FORMAT[] = "%H:%M"; 317 static constexpr int CLOCK_LENGTH = 6; 318 319 const animation::text_field& field = anim.text_clock; 320 321 if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) return; 322 323 time_t rawtime; 324 time(&rawtime); 325 struct tm* time_info = localtime(&rawtime); 326 327 char clock_str[CLOCK_LENGTH]; 328 size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info); 329 if (length != CLOCK_LENGTH - 1) { 330 LOGE("Could not format time\n"); 331 return; 332 } 333 334 int x, y; 335 determine_xy(field, length, &x, &y); 336 337 LOGV("drawing clock %s %d %d\n", clock_str, x, y); 338 gr_color(field.color_r, field.color_g, field.color_b, field.color_a); 339 gr_text(field.font, x, y, clock_str, false); 340} 341 342static void draw_percent(const animation& anim) 343{ 344 if (anim.cur_level <= 0 || anim.cur_status != BATTERY_STATUS_CHARGING) return; 345 346 const animation::text_field& field = anim.text_percent; 347 if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) { 348 return; 349 } 350 351 std::string str = base::StringPrintf("%d%%", anim.cur_level); 352 353 int x, y; 354 determine_xy(field, str.size(), &x, &y); 355 356 LOGV("drawing percent %s %d %d\n", str.c_str(), x, y); 357 gr_color(field.color_r, field.color_g, field.color_b, field.color_a); 358 gr_text(field.font, x, y, str.c_str(), false); 359} 360 361/* returns the last y-offset of where the surface ends */ 362static int draw_surface_centered(GRSurface* surface) 363{ 364 int w; 365 int h; 366 int x; 367 int y; 368 369 w = gr_get_width(surface); 370 h = gr_get_height(surface); 371 x = (gr_fb_width() - w) / 2 ; 372 y = (gr_fb_height() - h) / 2 ; 373 374 LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y); 375 gr_blit(surface, 0, 0, w, h, x, y); 376 return y + h; 377} 378 379static void draw_unknown(struct charger *charger) 380{ 381 int y; 382 if (charger->surf_unknown) { 383 draw_surface_centered(charger->surf_unknown); 384 } else { 385 android_green(); 386 y = draw_text("Charging!", -1, -1); 387 draw_text("?\?/100", -1, y + 25); 388 } 389} 390 391static void draw_battery(const struct charger* charger) 392{ 393 const struct animation& anim = *charger->batt_anim; 394 const struct animation::frame& frame = anim.frames[anim.cur_frame]; 395 396 if (anim.num_frames != 0) { 397 draw_surface_centered(frame.surface); 398 LOGV("drawing frame #%d min_cap=%d time=%d\n", 399 anim.cur_frame, frame.min_level, 400 frame.disp_time); 401 } 402 draw_clock(anim); 403 draw_percent(anim); 404} 405 406static void redraw_screen(struct charger *charger) 407{ 408 struct animation *batt_anim = charger->batt_anim; 409 410 clear_screen(); 411 412 /* try to display *something* */ 413 if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0) 414 draw_unknown(charger); 415 else 416 draw_battery(charger); 417 gr_flip(); 418} 419 420static void kick_animation(struct animation *anim) 421{ 422 anim->run = true; 423} 424 425static void reset_animation(struct animation *anim) 426{ 427 anim->cur_cycle = 0; 428 anim->cur_frame = 0; 429 anim->run = false; 430} 431 432static void init_status_display(struct animation* anim) 433{ 434 int res; 435 436 if (!anim->text_clock.font_file.empty()) { 437 if ((res = 438 gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) { 439 LOGE("Could not load time font (%d)\n", res); 440 } 441 } 442 443 if (!anim->text_percent.font_file.empty()) { 444 if ((res = 445 gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) { 446 LOGE("Could not load percent font (%d)\n", res); 447 } 448 } 449} 450 451static void update_screen_state(struct charger *charger, int64_t now) 452{ 453 struct animation *batt_anim = charger->batt_anim; 454 int disp_time; 455 456 if (!batt_anim->run || now < charger->next_screen_transition) return; 457 458 if (!minui_inited) { 459 if (healthd_config && healthd_config->screen_on) { 460 if (!healthd_config->screen_on(batt_prop)) { 461 LOGV("[%" PRId64 "] leave screen off\n", now); 462 batt_anim->run = false; 463 charger->next_screen_transition = -1; 464 if (charger->charger_connected) 465 request_suspend(true); 466 return; 467 } 468 } 469 470 gr_init(); 471 gr_font_size(gr_sys_font(), &char_width, &char_height); 472 init_status_display(batt_anim); 473 474#ifndef CHARGER_DISABLE_INIT_BLANK 475 gr_fb_blank(true); 476#endif 477 minui_inited = true; 478 } 479 480 /* animation is over, blank screen and leave */ 481 if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) { 482 reset_animation(batt_anim); 483 charger->next_screen_transition = -1; 484 gr_fb_blank(true); 485 LOGV("[%" PRId64 "] animation done\n", now); 486 if (charger->charger_connected) 487 request_suspend(true); 488 return; 489 } 490 491 disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time; 492 493 /* animation starting, set up the animation */ 494 if (batt_anim->cur_frame == 0) { 495 496 LOGV("[%" PRId64 "] animation starting\n", now); 497 if (batt_prop) { 498 batt_anim->cur_level = batt_prop->batteryLevel; 499 batt_anim->cur_status = batt_prop->batteryStatus; 500 if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) { 501 /* find first frame given current battery level */ 502 for (int i = 0; i < batt_anim->num_frames; i++) { 503 if (batt_anim->cur_level >= batt_anim->frames[i].min_level && 504 batt_anim->cur_level <= batt_anim->frames[i].max_level) { 505 batt_anim->cur_frame = i; 506 break; 507 } 508 } 509 510 // repeat the first frame first_frame_repeats times 511 disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 512 batt_anim->first_frame_repeats; 513 } 514 } 515 } 516 517 /* unblank the screen on first cycle */ 518 if (batt_anim->cur_cycle == 0) 519 gr_fb_blank(false); 520 521 /* draw the new frame (@ cur_frame) */ 522 redraw_screen(charger); 523 524 /* if we don't have anim frames, we only have one image, so just bump 525 * the cycle counter and exit 526 */ 527 if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) { 528 LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now); 529 charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME; 530 batt_anim->cur_cycle++; 531 return; 532 } 533 534 /* schedule next screen transition */ 535 charger->next_screen_transition = now + disp_time; 536 537 /* advance frame cntr to the next valid frame only if we are charging 538 * if necessary, advance cycle cntr, and reset frame cntr 539 */ 540 if (charger->charger_connected) { 541 batt_anim->cur_frame++; 542 543 while (batt_anim->cur_frame < batt_anim->num_frames && 544 (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level || 545 batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) { 546 batt_anim->cur_frame++; 547 } 548 if (batt_anim->cur_frame >= batt_anim->num_frames) { 549 batt_anim->cur_cycle++; 550 batt_anim->cur_frame = 0; 551 552 /* don't reset the cycle counter, since we use that as a signal 553 * in a test above to check if animation is over 554 */ 555 } 556 } else { 557 /* Stop animating if we're not charging. 558 * If we stop it immediately instead of going through this loop, then 559 * the animation would stop somewhere in the middle. 560 */ 561 batt_anim->cur_frame = 0; 562 batt_anim->cur_cycle++; 563 } 564} 565 566static int set_key_callback(int code, int value, void *data) 567{ 568 struct charger *charger = (struct charger *)data; 569 int64_t now = curr_time_ms(); 570 int down = !!value; 571 572 if (code > KEY_MAX) 573 return -1; 574 575 /* ignore events that don't modify our state */ 576 if (charger->keys[code].down == down) 577 return 0; 578 579 /* only record the down even timestamp, as the amount 580 * of time the key spent not being pressed is not useful */ 581 if (down) 582 charger->keys[code].timestamp = now; 583 charger->keys[code].down = down; 584 charger->keys[code].pending = true; 585 if (down) { 586 LOGV("[%" PRId64 "] key[%d] down\n", now, code); 587 } else { 588 int64_t duration = now - charger->keys[code].timestamp; 589 int64_t secs = duration / 1000; 590 int64_t msecs = duration - secs * 1000; 591 LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", 592 now, code, secs, msecs); 593 } 594 595 return 0; 596} 597 598static void update_input_state(struct charger *charger, 599 struct input_event *ev) 600{ 601 if (ev->type != EV_KEY) 602 return; 603 set_key_callback(ev->code, ev->value, charger); 604} 605 606static void set_next_key_check(struct charger *charger, 607 struct key_state *key, 608 int64_t timeout) 609{ 610 int64_t then = key->timestamp + timeout; 611 612 if (charger->next_key_check == -1 || then < charger->next_key_check) 613 charger->next_key_check = then; 614} 615 616static void process_key(struct charger *charger, int code, int64_t now) 617{ 618 struct key_state *key = &charger->keys[code]; 619 620 if (code == KEY_POWER) { 621 if (key->down) { 622 int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME; 623 if (now >= reboot_timeout) { 624 /* We do not currently support booting from charger mode on 625 all devices. Check the property and continue booting or reboot 626 accordingly. */ 627 if (property_get_bool("ro.enable_boot_charger_mode", false)) { 628 LOGW("[%" PRId64 "] booting from charger mode\n", now); 629 property_set("sys.boot_from_charger_mode", "1"); 630 } else { 631 if (charger->batt_anim->cur_level >= charger->boot_min_cap) { 632 LOGW("[%" PRId64 "] rebooting\n", now); 633 android_reboot(ANDROID_RB_RESTART, 0, 0); 634 } else { 635 LOGV("[%" PRId64 "] ignore power-button press, battery level " 636 "less than minimum\n", now); 637 } 638 } 639 } else { 640 /* if the key is pressed but timeout hasn't expired, 641 * make sure we wake up at the right-ish time to check 642 */ 643 set_next_key_check(charger, key, POWER_ON_KEY_TIME); 644 645 /* Turn on the display and kick animation on power-key press 646 * rather than on key release 647 */ 648 kick_animation(charger->batt_anim); 649 request_suspend(false); 650 } 651 } else { 652 /* if the power key got released, force screen state cycle */ 653 if (key->pending) { 654 kick_animation(charger->batt_anim); 655 } 656 } 657 } 658 659 key->pending = false; 660} 661 662static void handle_input_state(struct charger *charger, int64_t now) 663{ 664 process_key(charger, KEY_POWER, now); 665 666 if (charger->next_key_check != -1 && now > charger->next_key_check) 667 charger->next_key_check = -1; 668} 669 670static void handle_power_supply_state(struct charger *charger, int64_t now) 671{ 672 if (!charger->have_battery_state) 673 return; 674 675 if (!charger->charger_connected) { 676 677 /* Last cycle would have stopped at the extreme top of battery-icon 678 * Need to show the correct level corresponding to capacity. 679 */ 680 kick_animation(charger->batt_anim); 681 request_suspend(false); 682 if (charger->next_pwr_check == -1) { 683 charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME; 684 LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n", 685 now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check); 686 } else if (now >= charger->next_pwr_check) { 687 LOGW("[%" PRId64 "] shutting down\n", now); 688 android_reboot(ANDROID_RB_POWEROFF, 0, 0); 689 } else { 690 /* otherwise we already have a shutdown timer scheduled */ 691 } 692 } else { 693 /* online supply present, reset shutdown timer if set */ 694 if (charger->next_pwr_check != -1) { 695 LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now); 696 kick_animation(charger->batt_anim); 697 } 698 charger->next_pwr_check = -1; 699 } 700} 701 702void healthd_mode_charger_heartbeat() 703{ 704 struct charger *charger = &charger_state; 705 int64_t now = curr_time_ms(); 706 707 handle_input_state(charger, now); 708 handle_power_supply_state(charger, now); 709 710 /* do screen update last in case any of the above want to start 711 * screen transitions (animations, etc) 712 */ 713 update_screen_state(charger, now); 714} 715 716void healthd_mode_charger_battery_update( 717 struct android::BatteryProperties *props) 718{ 719 struct charger *charger = &charger_state; 720 721 charger->charger_connected = 722 props->chargerAcOnline || props->chargerUsbOnline || 723 props->chargerWirelessOnline; 724 725 if (!charger->have_battery_state) { 726 charger->have_battery_state = true; 727 charger->next_screen_transition = curr_time_ms() - 1; 728 reset_animation(charger->batt_anim); 729 kick_animation(charger->batt_anim); 730 } 731 batt_prop = props; 732} 733 734int healthd_mode_charger_preparetowait(void) 735{ 736 struct charger *charger = &charger_state; 737 int64_t now = curr_time_ms(); 738 int64_t next_event = INT64_MAX; 739 int64_t timeout; 740 741 LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n", now, 742 charger->next_screen_transition, charger->next_key_check, 743 charger->next_pwr_check); 744 745 if (charger->next_screen_transition != -1) 746 next_event = charger->next_screen_transition; 747 if (charger->next_key_check != -1 && charger->next_key_check < next_event) 748 next_event = charger->next_key_check; 749 if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event) 750 next_event = charger->next_pwr_check; 751 752 if (next_event != -1 && next_event != INT64_MAX) 753 timeout = max(0, next_event - now); 754 else 755 timeout = -1; 756 757 return (int)timeout; 758} 759 760static int input_callback(int fd, unsigned int epevents, void *data) 761{ 762 struct charger *charger = (struct charger *)data; 763 struct input_event ev; 764 int ret; 765 766 ret = ev_get_input(fd, epevents, &ev); 767 if (ret) 768 return -1; 769 update_input_state(charger, &ev); 770 return 0; 771} 772 773static void charger_event_handler(uint32_t /*epevents*/) 774{ 775 int ret; 776 777 ret = ev_wait(-1); 778 if (!ret) 779 ev_dispatch(); 780} 781 782animation* init_animation() 783{ 784 bool parse_success; 785 786 std::string content; 787 if (base::ReadFileToString(animation_desc_path, &content)) { 788 parse_success = parse_animation_desc(content, &battery_animation); 789 } else { 790 LOGW("Could not open animation description at %s\n", animation_desc_path); 791 parse_success = false; 792 } 793 794 if (!parse_success) { 795 LOGW("Could not parse animation description. Using default animation.\n"); 796 battery_animation = BASE_ANIMATION; 797 battery_animation.animation_file.assign("charger/battery_scale"); 798 battery_animation.frames = default_animation_frames; 799 battery_animation.num_frames = ARRAY_SIZE(default_animation_frames); 800 } 801 if (battery_animation.fail_file.empty()) { 802 battery_animation.fail_file.assign("charger/battery_fail"); 803 } 804 805 LOGV("Animation Description:\n"); 806 LOGV(" animation: %d %d '%s' (%d)\n", 807 battery_animation.num_cycles, battery_animation.first_frame_repeats, 808 battery_animation.animation_file.c_str(), battery_animation.num_frames); 809 LOGV(" fail_file: '%s'\n", battery_animation.fail_file.c_str()); 810 LOGV(" clock: %d %d %d %d %d %d '%s'\n", 811 battery_animation.text_clock.pos_x, battery_animation.text_clock.pos_y, 812 battery_animation.text_clock.color_r, battery_animation.text_clock.color_g, 813 battery_animation.text_clock.color_b, battery_animation.text_clock.color_a, 814 battery_animation.text_clock.font_file.c_str()); 815 LOGV(" percent: %d %d %d %d %d %d '%s'\n", 816 battery_animation.text_percent.pos_x, battery_animation.text_percent.pos_y, 817 battery_animation.text_percent.color_r, battery_animation.text_percent.color_g, 818 battery_animation.text_percent.color_b, battery_animation.text_percent.color_a, 819 battery_animation.text_percent.font_file.c_str()); 820 for (int i = 0; i < battery_animation.num_frames; i++) { 821 LOGV(" frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time, 822 battery_animation.frames[i].min_level, battery_animation.frames[i].max_level); 823 } 824 825 return &battery_animation; 826} 827 828void healthd_mode_charger_init(struct healthd_config* config) 829{ 830 int ret; 831 struct charger *charger = &charger_state; 832 int i; 833 int epollfd; 834 835 dump_last_kmsg(); 836 837 LOGW("--------------- STARTING CHARGER MODE ---------------\n"); 838 839 ret = ev_init(input_callback, charger); 840 if (!ret) { 841 epollfd = ev_get_epollfd(); 842 healthd_register_event(epollfd, charger_event_handler); 843 } 844 845 struct animation* anim = init_animation(); 846 charger->batt_anim = anim; 847 848 ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown); 849 if (ret < 0) { 850 LOGE("Cannot load custom battery_fail image. Reverting to built in.\n"); 851 ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown); 852 if (ret < 0) { 853 LOGE("Cannot load built in battery_fail image\n"); 854 charger->surf_unknown = NULL; 855 } 856 } 857 858 GRSurface** scale_frames; 859 int scale_count; 860 int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text 861 // chunk). We are using hard-coded frame.disp_time instead. 862 ret = res_create_multi_display_surface(anim->animation_file.c_str(), 863 &scale_count, &scale_fps, &scale_frames); 864 if (ret < 0) { 865 LOGE("Cannot load battery_scale image\n"); 866 anim->num_frames = 0; 867 anim->num_cycles = 1; 868 } else if (scale_count != anim->num_frames) { 869 LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", 870 scale_count, anim->num_frames); 871 anim->num_frames = 0; 872 anim->num_cycles = 1; 873 } else { 874 for (i = 0; i < anim->num_frames; i++) { 875 anim->frames[i].surface = scale_frames[i]; 876 } 877 } 878 ev_sync_key_state(set_key_callback, charger); 879 880 charger->next_screen_transition = -1; 881 charger->next_key_check = -1; 882 charger->next_pwr_check = -1; 883 healthd_config = config; 884 charger->boot_min_cap = config->boot_min_cap; 885} 886