blob: ea0212a3b39a390b11cfb52d3f7c40584684f495 [file] [log] [blame]
Steve Kondik75956202016-08-07 23:21:29 -07001/*
2 * Copyright (C) 2016 The CyanogenMod Project
Zhao Wei Liewc8537522017-02-05 12:52:35 +08003 * 2017 The LineageOS Project
Steve Kondik75956202016-08-07 23:21:29 -07004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <errno.h>
19#include <fcntl.h>
20#include <stdbool.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <time.h>
27#include <unistd.h>
28
29#include <cutils/android_reboot.h>
30#include <cutils/klog.h>
31#include <cutils/misc.h>
32#include <cutils/uevent.h>
33#include <cutils/properties.h>
34
35#include <pthread.h>
36#include <linux/android_alarm.h>
37#include <sys/timerfd.h>
38#include <linux/rtc.h>
39
Luca Stefani7d2b6d22016-08-25 14:41:17 +020040#include "healthd/healthd.h"
Steve Kondik75956202016-08-07 23:21:29 -070041#include "minui/minui.h"
42
43#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
44#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
45#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0)
46#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
47
Michael Wea82b9f2017-07-22 21:53:47 +020048static const GRFont* gr_font = NULL;
49
Steve Kondik75956202016-08-07 23:21:29 -070050struct frame {
51 int min_capacity;
52 GRSurface *surface;
53};
54
55struct animation {
56 struct frame *frames;
57 int cur_frame;
58 int num_frames;
59};
60
61static struct animation anim = {
62 .frames = NULL,
63 .cur_frame = 0,
64 .num_frames = 0,
65};
66
Michael Wea82b9f2017-07-22 21:53:47 +020067static const GRFont* get_font()
68{
69 return gr_font;
70}
71
Steve Kondik75956202016-08-07 23:21:29 -070072static int draw_surface_centered(GRSurface* surface)
73{
74 int w, h, x, y;
75
76 w = gr_get_width(surface);
77 h = gr_get_height(surface);
78 x = (gr_fb_width() - w) / 2 ;
79 y = (gr_fb_height() - h) / 2 ;
80
81 gr_blit(surface, 0, 0, w, h, x, y);
82 return y + h;
83}
84
85#define STR_LEN 64
86static void draw_capacity(int capacity)
87{
Steve Kondik75956202016-08-07 23:21:29 -070088 char cap_str[STR_LEN];
89 snprintf(cap_str, (STR_LEN - 1), "%d%%", capacity);
Michael W4cc370e2016-08-16 13:06:45 +020090
91 struct frame *f = &anim.frames[0];
92 int font_x, font_y;
Michael Wea82b9f2017-07-22 21:53:47 +020093 gr_font_size(get_font(), &font_x, &font_y);
94 int w = gr_measure(get_font(), cap_str);
Michael W4cc370e2016-08-16 13:06:45 +020095 int h = gr_get_height(f->surface);
96 int x = (gr_fb_width() - w) / 2;
97 int y = (gr_fb_height() + h) / 2;
98
Steve Kondik75956202016-08-07 23:21:29 -070099 gr_color(255, 255, 255, 255);
Michael Wea82b9f2017-07-22 21:53:47 +0200100 gr_text(get_font(), x, y + font_y / 2, cap_str, 0);
Steve Kondik75956202016-08-07 23:21:29 -0700101}
102
103#ifdef QCOM_HARDWARE
104enum alarm_time_type {
105 ALARM_TIME,
106 RTC_TIME,
107};
108
109/*
110 * shouldn't be changed after
111 * reading from alarm register
112 */
113static time_t alm_secs;
114
115static int alarm_get_time(enum alarm_time_type time_type,
116 time_t *secs)
117{
118 struct tm tm;
119 unsigned int cmd;
120 int rc, fd = -1;
121
122 if (!secs)
123 return -1;
124
125 fd = open("/dev/rtc0", O_RDONLY);
126 if (fd < 0) {
127 LOGE("Can't open rtc devfs node\n");
128 return -1;
129 }
130
131 switch (time_type) {
132 case ALARM_TIME:
133 cmd = RTC_ALM_READ;
134 break;
135 case RTC_TIME:
136 cmd = RTC_RD_TIME;
137 break;
138 default:
139 LOGE("Invalid time type\n");
140 goto err;
141 }
142
143 rc = ioctl(fd, cmd, &tm);
144 if (rc < 0) {
145 LOGE("Unable to get time\n");
146 goto err;
147 }
148
149 *secs = mktime(&tm) + tm.tm_gmtoff;
150 if (*secs < 0) {
151 LOGE("Invalid seconds = %ld\n", *secs);
152 goto err;
153 }
154
155 close(fd);
156 return 0;
157
158err:
159 close(fd);
160 return -1;
161}
162
163#define ERR_SECS 2
164static int alarm_is_alm_expired()
165{
166 int rc;
167 time_t rtc_secs;
168
169 rc = alarm_get_time(RTC_TIME, &rtc_secs);
170 if (rc < 0)
171 return 0;
172
173 return (alm_secs >= rtc_secs - ERR_SECS &&
174 alm_secs <= rtc_secs + ERR_SECS) ? 1 : 0;
175}
176
177static int timerfd_set_reboot_time_and_wait(time_t secs)
178{
179 int fd;
180 int ret = -1;
181 fd = timerfd_create(CLOCK_REALTIME_ALARM, 0);
182 if (fd < 0) {
183 LOGE("Can't open timerfd alarm node\n");
184 goto err_return;
185 }
186
187 struct itimerspec spec;
188 memset(&spec, 0, sizeof(spec));
189 spec.it_value.tv_sec = secs;
190
191 if (timerfd_settime(fd, 0 /* relative */, &spec, NULL)) {
192 LOGE("Can't set timerfd alarm\n");
193 goto err_close;
194 }
195
196 uint64_t unused;
197 if (read(fd, &unused, sizeof(unused)) < 0) {
198 LOGE("Wait alarm error\n");
199 goto err_close;
200 }
201
202 ret = 0;
203err_close:
204 close(fd);
205err_return:
206 return ret;
207}
208
209static int alarm_set_reboot_time_and_wait(time_t secs)
210{
211 int rc, fd;
212 struct timespec ts;
213
214 fd = open("/dev/alarm", O_RDWR);
215 if (fd < 0) {
216 LOGE("Can't open alarm devfs node, trying timerfd\n");
217 return timerfd_set_reboot_time_and_wait(secs);
218 }
219
220 /* get the elapsed realtime from boot time to now */
221 rc = ioctl(fd, ANDROID_ALARM_GET_TIME(
222 ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts);
223 if (rc < 0) {
224 LOGE("Unable to get elapsed realtime\n");
225 goto err;
226 }
227
228 /* calculate the elapsed time from boot time to reboot time */
229 ts.tv_sec += secs;
230 ts.tv_nsec = 0;
231
232 rc = ioctl(fd, ANDROID_ALARM_SET(
233 ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts);
234 if (rc < 0) {
235 LOGE("Unable to set reboot time to %ld\n", secs);
236 goto err;
237 }
238
239 do {
240 rc = ioctl(fd, ANDROID_ALARM_WAIT);
241 } while ((rc < 0 && errno == EINTR) || !alarm_is_alm_expired());
242
243 if (rc <= 0) {
244 LOGE("Unable to wait on alarm\n");
245 goto err;
246 }
247
248 close(fd);
249 return 0;
250
251err:
252 if (fd >= 0)
253 close(fd);
254 return -1;
255}
256
257static void *alarm_thread(void *)
258{
259 time_t rtc_secs, rb_secs;
260 int rc;
261
262 /*
263 * to support power off alarm, the time
264 * stored in alarm register at latest
265 * shutdown time should be some time
266 * earlier than the actual alarm time
267 * set by user
268 */
269 rc = alarm_get_time(ALARM_TIME, &alm_secs);
Adrian DCe91605c2016-08-14 11:40:05 +0200270 LOGI("RTC Alarm %ld\n", alm_secs);
Steve Kondik75956202016-08-07 23:21:29 -0700271 if (rc < 0 || !alm_secs)
272 goto err;
273
274 rc = alarm_get_time(RTC_TIME, &rtc_secs);
Adrian DCe91605c2016-08-14 11:40:05 +0200275 LOGI("RTC Clock %ld\n", rtc_secs);
Steve Kondik75956202016-08-07 23:21:29 -0700276 if (rc < 0)
277 goto err;
278
279 /*
280 * calculate the reboot time after which
281 * the phone will reboot
282 */
283 rb_secs = alm_secs - rtc_secs;
284 if (rb_secs <= 0)
285 goto err;
286
287 rc = alarm_set_reboot_time_and_wait(rb_secs);
288 if (rc < 0)
289 goto err;
290
291 LOGI("Exit from power off charging, reboot the phone!\n");
292 android_reboot(ANDROID_RB_RESTART, 0, 0);
293
294err:
295 LOGE("Exit from alarm thread\n");
296 return NULL;
297}
298#endif
299
300void healthd_board_init(struct healthd_config*)
301{
302 pthread_t tid;
303 char value[PROP_VALUE_MAX];
304 int rc = 0, scale_count = 0, i;
305 GRSurface **scale_frames;
Luca Stefani5c60e4f2017-08-17 19:28:48 +0200306 int scale_fps; // Not in use (charger/lineage_battery_scale doesn't have FPS text
Luca Stefani7d2b6d22016-08-25 14:41:17 +0200307 // chunk). We are using hard-coded frame.disp_time instead.
Steve Kondik75956202016-08-07 23:21:29 -0700308
Luca Stefani5c60e4f2017-08-17 19:28:48 +0200309 rc = res_create_multi_display_surface("charger/lineage_battery_scale",
Zhao Wei Liewc8537522017-02-05 12:52:35 +0800310 &scale_count, &scale_fps, &scale_frames);
Steve Kondik75956202016-08-07 23:21:29 -0700311 if (rc < 0) {
312 LOGE("%s: Unable to load battery scale image", __func__);
313 return;
314 }
315
316 anim.frames = new frame[scale_count];
317 anim.num_frames = scale_count;
318 for (i = 0; i < anim.num_frames; i++) {
319 anim.frames[i].surface = scale_frames[i];
320 anim.frames[i].min_capacity = 100/(scale_count-1) * i;
321 }
322
323#ifdef QCOM_HARDWARE
324 property_get("ro.bootmode", value, "");
325 if (!strcmp("charger", value)) {
326 rc = pthread_create(&tid, NULL, alarm_thread, NULL);
327 if (rc < 0)
328 LOGE("Create alarm thread failed\n");
329 }
330#endif
331}
332
333int healthd_board_battery_update(struct android::BatteryProperties*)
334{
335 // return 0 to log periodic polled battery status to kernel log
336 return 1;
337}
338
339void healthd_board_mode_charger_draw_battery(
340 struct android::BatteryProperties *batt_prop)
341{
342 int start_frame = 0;
343 int capacity = -1;
344
Steve Kondik75956202016-08-07 23:21:29 -0700345 if (batt_prop && batt_prop->batteryLevel >= 0) {
346 capacity = batt_prop->batteryLevel;
347 }
348
349 if (anim.num_frames == 0 || capacity < 0) {
350 LOGE("%s: Unable to draw battery", __func__);
351 return;
352 }
353
354 // Find starting frame to display based on current capacity
355 for (start_frame = 1; start_frame < anim.num_frames; start_frame++) {
356 if (capacity < anim.frames[start_frame].min_capacity)
357 break;
358 }
359 // Always start from the level just below the current capacity
360 start_frame--;
361
362 if (anim.cur_frame < start_frame)
363 anim.cur_frame = start_frame;
364
365 draw_surface_centered(anim.frames[anim.cur_frame].surface);
366 draw_capacity(capacity);
367 // Move to next frame, with max possible frame at max_idx
368 anim.cur_frame = ((anim.cur_frame + 1) % anim.num_frames);
369}
370
371void healthd_board_mode_charger_battery_update(
372 struct android::BatteryProperties*)
373{
374}
375
Adrian DCcadd0be2016-08-14 11:44:31 +0200376#ifdef HEALTHD_BACKLIGHT_PATH
377#ifndef HEALTHD_BACKLIGHT_LEVEL
378#define HEALTHD_BACKLIGHT_LEVEL 100
379#endif
380
381void healthd_board_mode_charger_set_backlight(bool on)
382{
383 int fd;
384 char buffer[10];
385
386 memset(buffer, '\0', sizeof(buffer));
387 fd = open(HEALTHD_BACKLIGHT_PATH, O_RDWR);
388 if (fd < 0) {
389 LOGE("Could not open backlight node : %s\n", strerror(errno));
390 return;
391 }
392 LOGV("Enabling backlight\n");
393 snprintf(buffer, sizeof(buffer), "%d\n", on ? HEALTHD_BACKLIGHT_LEVEL : 0);
394 if (write(fd, buffer, strlen(buffer)) < 0) {
395 LOGE("Could not write to backlight : %s\n", strerror(errno));
396 }
397 close(fd);
398
399#ifdef HEALTHD_SECONDARY_BACKLIGHT_PATH
400 fd = open(HEALTHD_SECONDARY_BACKLIGHT_PATH, O_RDWR);
401 if (fd < 0) {
402 LOGE("Could not open second backlight node : %s\n", strerror(errno));
403 return;
404 }
405 LOGV("Enabling secondary backlight\n");
406 if (write(fd, buffer, strlen(buffer)) < 0) {
407 LOGE("Could not write to second backlight : %s\n", strerror(errno));
408 return;
409 }
410 close(fd);
411#endif
412}
413
414#else
Steve Kondik75956202016-08-07 23:21:29 -0700415void healthd_board_mode_charger_set_backlight(bool)
416{
417}
Adrian DCcadd0be2016-08-14 11:44:31 +0200418#endif
Steve Kondik75956202016-08-07 23:21:29 -0700419
420void healthd_board_mode_charger_init(void)
421{
Michael Wea82b9f2017-07-22 21:53:47 +0200422 GRFont* tmp_font;
423 int res = gr_init_font("font_log", &tmp_font);
424 if (res == 0) {
425 gr_font = tmp_font;
426 } else {
427 LOGW("Couldn't open font, falling back to default!\n");
428 gr_font = gr_sys_font();
429 }
430
Steve Kondik75956202016-08-07 23:21:29 -0700431}