diff --git a/charger/Android.mk b/charger/Android.mk new file mode 100644 index 00000000..0f5e55fa --- /dev/null +++ b/charger/Android.mk @@ -0,0 +1,47 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := healthd_board_cm.cpp +LOCAL_MODULE := libhealthd.cm +LOCAL_CFLAGS := -Werror +LOCAL_C_INCLUDES := \ + system/core/healthd \ + bootable/recovery +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := font_log.png +LOCAL_SRC_FILES := fonts/$(PRODUCT_AAPT_PREF_CONFIG)/font_log.png +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/res/images +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) + +define _add-charger-image +include $$(CLEAR_VARS) +LOCAL_MODULE := vendor_cm_charger_$(notdir $(1)) +LOCAL_MODULE_STEM := $(notdir $(1)) +_img_modules += $$(LOCAL_MODULE) +LOCAL_SRC_FILES := $1 +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger +include $$(BUILD_PREBUILT) +endef + +_img_modules := +_images := +$(foreach _img, $(call find-subdir-subdir-files, "images/$(PRODUCT_AAPT_PREF_CONFIG)", "*.png"), \ + $(eval $(call _add-charger-image,$(_img)))) + +include $(CLEAR_VARS) +LOCAL_MODULE := cm_charger_res_images +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := $(_img_modules) +LOCAL_OVERRIDES_PACKAGES := charger_res_images +include $(BUILD_PHONY_PACKAGE) + +_add-charger-image := +_img_modules := diff --git a/charger/fonts/560dpi b/charger/fonts/560dpi new file mode 120000 index 00000000..17ef829d --- /dev/null +++ b/charger/fonts/560dpi @@ -0,0 +1 @@ +xxhdpi \ No newline at end of file diff --git a/charger/fonts/hdpi/font_log.png b/charger/fonts/hdpi/font_log.png new file mode 100644 index 00000000..ddf9bc7a Binary files /dev/null and b/charger/fonts/hdpi/font_log.png differ diff --git a/charger/fonts/mdpi/font_log.png b/charger/fonts/mdpi/font_log.png new file mode 100644 index 00000000..d727fa1f Binary files /dev/null and b/charger/fonts/mdpi/font_log.png differ diff --git a/charger/fonts/xhdpi/font_log.png b/charger/fonts/xhdpi/font_log.png new file mode 100644 index 00000000..9c16b772 Binary files /dev/null and b/charger/fonts/xhdpi/font_log.png differ diff --git a/charger/fonts/xxhdpi/font_log.png b/charger/fonts/xxhdpi/font_log.png new file mode 100644 index 00000000..5d122b72 Binary files /dev/null and b/charger/fonts/xxhdpi/font_log.png differ diff --git a/charger/fonts/xxxhdpi/font_log.png b/charger/fonts/xxxhdpi/font_log.png new file mode 100644 index 00000000..60dacec1 Binary files /dev/null and b/charger/fonts/xxxhdpi/font_log.png differ diff --git a/charger/healthd_board_cm.cpp b/charger/healthd_board_cm.cpp new file mode 100644 index 00000000..c5aa3e64 --- /dev/null +++ b/charger/healthd_board_cm.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "healthd.h" +#include "minui/minui.h" + +#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) +#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0) +#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0) +#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) + +struct frame { + int min_capacity; + GRSurface *surface; +}; + +struct animation { + struct frame *frames; + int cur_frame; + int num_frames; +}; + +static struct animation anim = { + .frames = NULL, + .cur_frame = 0, + .num_frames = 0, +}; + +static bool font_inited; + +static int draw_surface_centered(GRSurface* surface) +{ + int w, h, x, y; + + w = gr_get_width(surface); + h = gr_get_height(surface); + x = (gr_fb_width() - w) / 2 ; + y = (gr_fb_height() - h) / 2 ; + + gr_blit(surface, 0, 0, w, h, x, y); + return y + h; +} + +#define STR_LEN 64 +static void draw_capacity(int capacity) +{ + struct frame *f = &anim.frames[0]; + int w = gr_get_width(f->surface); + int h = gr_get_height(f->surface); + int x = (gr_fb_width() - w) / 2 ; + int y = (gr_fb_height() + h) / 2; + + char cap_str[STR_LEN]; + snprintf(cap_str, (STR_LEN - 1), "%d%%", capacity); + gr_color(255, 255, 255, 255); + gr_text(x, y * 1.05, cap_str, 0); +} + +#ifdef QCOM_HARDWARE +enum alarm_time_type { + ALARM_TIME, + RTC_TIME, +}; + +/* + * shouldn't be changed after + * reading from alarm register + */ +static time_t alm_secs; + +static int alarm_get_time(enum alarm_time_type time_type, + time_t *secs) +{ + struct tm tm; + unsigned int cmd; + int rc, fd = -1; + + if (!secs) + return -1; + + fd = open("/dev/rtc0", O_RDONLY); + if (fd < 0) { + LOGE("Can't open rtc devfs node\n"); + return -1; + } + + switch (time_type) { + case ALARM_TIME: + cmd = RTC_ALM_READ; + break; + case RTC_TIME: + cmd = RTC_RD_TIME; + break; + default: + LOGE("Invalid time type\n"); + goto err; + } + + rc = ioctl(fd, cmd, &tm); + if (rc < 0) { + LOGE("Unable to get time\n"); + goto err; + } + + *secs = mktime(&tm) + tm.tm_gmtoff; + if (*secs < 0) { + LOGE("Invalid seconds = %ld\n", *secs); + goto err; + } + + close(fd); + return 0; + +err: + close(fd); + return -1; +} + +#define ERR_SECS 2 +static int alarm_is_alm_expired() +{ + int rc; + time_t rtc_secs; + + rc = alarm_get_time(RTC_TIME, &rtc_secs); + if (rc < 0) + return 0; + + return (alm_secs >= rtc_secs - ERR_SECS && + alm_secs <= rtc_secs + ERR_SECS) ? 1 : 0; +} + +static int timerfd_set_reboot_time_and_wait(time_t secs) +{ + int fd; + int ret = -1; + fd = timerfd_create(CLOCK_REALTIME_ALARM, 0); + if (fd < 0) { + LOGE("Can't open timerfd alarm node\n"); + goto err_return; + } + + struct itimerspec spec; + memset(&spec, 0, sizeof(spec)); + spec.it_value.tv_sec = secs; + + if (timerfd_settime(fd, 0 /* relative */, &spec, NULL)) { + LOGE("Can't set timerfd alarm\n"); + goto err_close; + } + + uint64_t unused; + if (read(fd, &unused, sizeof(unused)) < 0) { + LOGE("Wait alarm error\n"); + goto err_close; + } + + ret = 0; +err_close: + close(fd); +err_return: + return ret; +} + +static int alarm_set_reboot_time_and_wait(time_t secs) +{ + int rc, fd; + struct timespec ts; + + fd = open("/dev/alarm", O_RDWR); + if (fd < 0) { + LOGE("Can't open alarm devfs node, trying timerfd\n"); + return timerfd_set_reboot_time_and_wait(secs); + } + + /* get the elapsed realtime from boot time to now */ + rc = ioctl(fd, ANDROID_ALARM_GET_TIME( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts); + if (rc < 0) { + LOGE("Unable to get elapsed realtime\n"); + goto err; + } + + /* calculate the elapsed time from boot time to reboot time */ + ts.tv_sec += secs; + ts.tv_nsec = 0; + + rc = ioctl(fd, ANDROID_ALARM_SET( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts); + if (rc < 0) { + LOGE("Unable to set reboot time to %ld\n", secs); + goto err; + } + + do { + rc = ioctl(fd, ANDROID_ALARM_WAIT); + } while ((rc < 0 && errno == EINTR) || !alarm_is_alm_expired()); + + if (rc <= 0) { + LOGE("Unable to wait on alarm\n"); + goto err; + } + + close(fd); + return 0; + +err: + if (fd >= 0) + close(fd); + return -1; +} + +static void *alarm_thread(void *) +{ + time_t rtc_secs, rb_secs; + int rc; + + /* + * to support power off alarm, the time + * stored in alarm register at latest + * shutdown time should be some time + * earlier than the actual alarm time + * set by user + */ + rc = alarm_get_time(ALARM_TIME, &alm_secs); + if (rc < 0 || !alm_secs) + goto err; + + rc = alarm_get_time(RTC_TIME, &rtc_secs); + if (rc < 0) + goto err; + + /* + * calculate the reboot time after which + * the phone will reboot + */ + rb_secs = alm_secs - rtc_secs; + if (rb_secs <= 0) + goto err; + + rc = alarm_set_reboot_time_and_wait(rb_secs); + if (rc < 0) + goto err; + + LOGI("Exit from power off charging, reboot the phone!\n"); + android_reboot(ANDROID_RB_RESTART, 0, 0); + +err: + LOGE("Exit from alarm thread\n"); + return NULL; +} +#endif + +void healthd_board_init(struct healthd_config*) +{ + pthread_t tid; + char value[PROP_VALUE_MAX]; + int rc = 0, scale_count = 0, i; + GRSurface **scale_frames; + + rc = res_create_multi_display_surface("charger/cm_battery_scale", + &scale_count, &scale_frames); + if (rc < 0) { + LOGE("%s: Unable to load battery scale image", __func__); + return; + } + + anim.frames = new frame[scale_count]; + anim.num_frames = scale_count; + for (i = 0; i < anim.num_frames; i++) { + anim.frames[i].surface = scale_frames[i]; + anim.frames[i].min_capacity = 100/(scale_count-1) * i; + } + +#ifdef QCOM_HARDWARE + property_get("ro.bootmode", value, ""); + if (!strcmp("charger", value)) { + rc = pthread_create(&tid, NULL, alarm_thread, NULL); + if (rc < 0) + LOGE("Create alarm thread failed\n"); + } +#endif +} + +int healthd_board_battery_update(struct android::BatteryProperties*) +{ + // return 0 to log periodic polled battery status to kernel log + return 1; +} + +void healthd_board_mode_charger_draw_battery( + struct android::BatteryProperties *batt_prop) +{ + int start_frame = 0; + int capacity = -1; + + if (!font_inited) { + gr_set_font("log"); + font_inited = true; + } + + if (batt_prop && batt_prop->batteryLevel >= 0) { + capacity = batt_prop->batteryLevel; + } + + if (anim.num_frames == 0 || capacity < 0) { + LOGE("%s: Unable to draw battery", __func__); + return; + } + + // Find starting frame to display based on current capacity + for (start_frame = 1; start_frame < anim.num_frames; start_frame++) { + if (capacity < anim.frames[start_frame].min_capacity) + break; + } + // Always start from the level just below the current capacity + start_frame--; + + if (anim.cur_frame < start_frame) + anim.cur_frame = start_frame; + + draw_surface_centered(anim.frames[anim.cur_frame].surface); + draw_capacity(capacity); + // Move to next frame, with max possible frame at max_idx + anim.cur_frame = ((anim.cur_frame + 1) % anim.num_frames); +} + +void healthd_board_mode_charger_battery_update( + struct android::BatteryProperties*) +{ +} + +void healthd_board_mode_charger_set_backlight(bool) +{ +} + +void healthd_board_mode_charger_init(void) +{ +} diff --git a/charger/images/560dpi b/charger/images/560dpi new file mode 120000 index 00000000..af2d1fe7 --- /dev/null +++ b/charger/images/560dpi @@ -0,0 +1 @@ +xxxhdpi \ No newline at end of file diff --git a/charger/images/hdpi/battery_fail.png b/charger/images/hdpi/battery_fail.png new file mode 100644 index 00000000..aded88a0 Binary files /dev/null and b/charger/images/hdpi/battery_fail.png differ diff --git a/charger/images/hdpi/cm_battery_scale.png b/charger/images/hdpi/cm_battery_scale.png new file mode 100644 index 00000000..0052c0ae Binary files /dev/null and b/charger/images/hdpi/cm_battery_scale.png differ diff --git a/charger/images/mdpi/battery_fail.png b/charger/images/mdpi/battery_fail.png new file mode 100644 index 00000000..aded88a0 Binary files /dev/null and b/charger/images/mdpi/battery_fail.png differ diff --git a/charger/images/mdpi/cm_battery_scale.png b/charger/images/mdpi/cm_battery_scale.png new file mode 100644 index 00000000..eb16a292 Binary files /dev/null and b/charger/images/mdpi/cm_battery_scale.png differ diff --git a/charger/images/xhdpi/battery_fail.png b/charger/images/xhdpi/battery_fail.png new file mode 100644 index 00000000..aded88a0 Binary files /dev/null and b/charger/images/xhdpi/battery_fail.png differ diff --git a/charger/images/xhdpi/cm_battery_scale.png b/charger/images/xhdpi/cm_battery_scale.png new file mode 100644 index 00000000..078b2a66 Binary files /dev/null and b/charger/images/xhdpi/cm_battery_scale.png differ diff --git a/charger/images/xxhdpi/battery_fail.png b/charger/images/xxhdpi/battery_fail.png new file mode 100644 index 00000000..aded88a0 Binary files /dev/null and b/charger/images/xxhdpi/battery_fail.png differ diff --git a/charger/images/xxhdpi/cm_battery_scale.png b/charger/images/xxhdpi/cm_battery_scale.png new file mode 100644 index 00000000..2e6553ed Binary files /dev/null and b/charger/images/xxhdpi/cm_battery_scale.png differ diff --git a/charger/images/xxxhdpi/battery_fail.png b/charger/images/xxxhdpi/battery_fail.png new file mode 100644 index 00000000..aded88a0 Binary files /dev/null and b/charger/images/xxxhdpi/battery_fail.png differ diff --git a/charger/images/xxxhdpi/cm_battery_scale.png b/charger/images/xxxhdpi/cm_battery_scale.png new file mode 100644 index 00000000..90c627d4 Binary files /dev/null and b/charger/images/xxxhdpi/cm_battery_scale.png differ diff --git a/config/BoardConfigCM.mk b/config/BoardConfigCM.mk new file mode 100644 index 00000000..a8bf1ad0 --- /dev/null +++ b/config/BoardConfigCM.mk @@ -0,0 +1,4 @@ +# Charger +ifneq ($(WITH_CM_CHARGER),false) + BOARD_HAL_STATIC_LIBRARIES := libhealthd.cm +endif diff --git a/config/common.mk b/config/common.mk index 2ee23dd2..a0e49cab 100644 --- a/config/common.mk +++ b/config/common.mk @@ -186,6 +186,16 @@ PRODUCT_PACKAGES += \ strace \ pigz +# Custom off-mode charger +ifneq ($(WITH_CM_CHARGER),false) +PRODUCT_PACKAGES += \ + charger_res_images \ + cm_charger_res_images \ + font_log.png \ + libhealthd.cm +endif + +# ExFAT support WITH_EXFAT ?= true ifeq ($(WITH_EXFAT),true) TARGET_USES_EXFAT := true