blob: f7e6f0fed7147e445ac80be28777a6c5d3f30c48 [file] [log] [blame]
David Ngda1fdc32018-03-22 23:44:56 -07001/*
2 * Copyright (C) 2014 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 <errno.h>
18#include <fcntl.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <time.h>
25#include <unistd.h>
26
27#include <sys/syscall.h>
28#include <sys/reboot.h>
29#include <sys/ioctl.h>
30#include <cutils/klog.h>
31#include <cutils/misc.h>
32#include <cutils/uevent.h>
33#include <cutils/properties.h>
34#include <pthread.h>
35#include <linux/rtc.h>
36#include <linux/time.h>
37#include <sys/epoll.h>
38#include <sys/timerfd.h>
39#include "healthd_msm.h"
40
41#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
42#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0)
43#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
44#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
45enum alarm_time_type {
46 ALARM_TIME,
47 RTC_TIME,
48};
49
50static int alarm_get_time(enum alarm_time_type time_type,
51 time_t *secs)
52{
53 struct tm tm;
54 unsigned int cmd;
55 int rc, fd = -1;
56
57 if (!secs)
58 return -1;
59
60 fd = open("/dev/rtc0", O_RDONLY);
61 if (fd < 0) {
62 LOGE("Can't open rtc devfs node\n");
63 return -1;
64 }
65
66 switch (time_type) {
67 case ALARM_TIME:
68 cmd = RTC_ALM_READ;
69 break;
70 case RTC_TIME:
71 cmd = RTC_RD_TIME;
72 break;
73 default:
74 LOGE("Invalid time type\n");
75 goto err;
76 }
77
78 rc = ioctl(fd, cmd, &tm);
79 if (rc < 0) {
80 LOGE("Unable to get time\n");
81 goto err;
82 }
83
84 *secs = mktime(&tm) + tm.tm_gmtoff;
85 if (*secs < 0) {
86 LOGE("Invalid seconds = %ld\n", *secs);
87 goto err;
88 }
89
90 close(fd);
91 return 0;
92
93err:
94 close(fd);
95 return -1;
96}
97
98static void alarm_reboot(void)
99{
100 LOGI("alarm time is up, reboot the phone!\n");
101 syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
102 LINUX_REBOOT_CMD_RESTART2, "rtc");
103}
104
105static int alarm_set_reboot_time_and_wait(time_t secs)
106{
107 int rc, epollfd, nevents;
108 int fd = 0;
109 struct timespec ts;
110 epoll_event event, events[1];
111 struct itimerspec itval;
112
113 epollfd = epoll_create(1);
114 if (epollfd < 0) {
115 LOGE("epoll_create failed\n");
116 goto err;
117 }
118
119 fd = timerfd_create(CLOCK_REALTIME_ALARM, 0);
120 if (fd < 0) {
121 LOGE("timerfd_create failed\n");
122 goto err;
123 }
124
125 event.events = EPOLLIN | EPOLLWAKEUP;
126 event.data.ptr = (void *)alarm_reboot;
127 rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
128 if (rc < 0) {
129 LOGE("epoll_ctl(EPOLL_CTL_ADD) failed \n");
130 goto err;
131 }
132
133 itval.it_value.tv_sec = secs;
134 itval.it_value.tv_nsec = 0;
135
136 itval.it_interval.tv_sec = 0;
137 itval.it_interval.tv_nsec = 0;
138
139 rc = timerfd_settime(fd, TFD_TIMER_ABSTIME, &itval, NULL);
140 if (rc < 0) {
141 LOGE("timerfd_settime failed %d\n",rc);
142 goto err;
143 }
144
145 nevents = epoll_wait(epollfd, events, 1, -1);
146
147 if (nevents <= 0) {
148 LOGE("Unable to wait on alarm\n");
149 goto err;
150 } else {
151 (*(void (*)())events[0].data.ptr)();
152 }
153
154 close(epollfd);
155 close(fd);
156 return 0;
157
158err:
159 if (epollfd > 0)
160 close(epollfd);
161
162 if (fd >= 0)
163 close(fd);
164 return -1;
165}
166
167/*
168 * 10s the estimated time from timestamp of alarm thread start
169 * to timestamp of android boot completed.
170 */
171#define TIME_DELTA 10
172
173/* seconds of 1 minute*/
174#define ONE_MINUTE 60
175static void *alarm_thread(void *)
176{
177 time_t rtc_secs, alarm_secs;
178 int rc;
179 timespec ts;
180
181 /*
182 * to support power off alarm, the time
183 * stored in alarm register at latest
184 * shutdown time should be some time
185 * earlier than the actual alarm time
186 * set by user
187 */
188 rc = alarm_get_time(ALARM_TIME, &alarm_secs);
189 if (rc < 0 || !alarm_secs)
190 goto err;
191
192 rc = alarm_get_time(RTC_TIME, &rtc_secs);
193 if (rc < 0 || !rtc_secs)
194 goto err;
195 LOGI("alarm time in rtc is %ld, rtc time is %ld\n", alarm_secs, rtc_secs);
196
197 if (alarm_secs <= rtc_secs) {
198 clock_gettime(CLOCK_BOOTTIME, &ts);
199
200 /*
201 * It is possible that last power off alarm time is up at this point.
202 * (alarm_secs + ONE_MINUTE) is the final alarm time to fire.
203 * (rtc_secs + ts.tv_sec + TIME_DELTA) is the estimated time of next
204 * boot completed to fire alarm.
205 * If the final alarm time is less than the estimated time of next boot
206 * completed to fire, that means it is not able to fire the last power
207 * off alarm at the right time, so just miss it.
208 */
209 if (alarm_secs + ONE_MINUTE < rtc_secs + ts.tv_sec + TIME_DELTA) {
210 LOGE("alarm is missed\n");
211 goto err;
212 }
213
214 alarm_reboot();
215 }
216
217 rc = alarm_set_reboot_time_and_wait(alarm_secs);
218 if (rc < 0)
219 goto err;
220
221err:
222 LOGE("Exit from alarm thread\n");
223 return NULL;
224}
225
226void power_off_alarm_init(void)
227{
228 pthread_t tid;
229 int rc;
230 char value[PROP_VALUE_MAX];
231
232 property_get("ro.bootmode", value, "");
233 if (!strcmp("charger", value)) {
234 rc = pthread_create(&tid, NULL, alarm_thread, NULL);
235 if (rc < 0)
236 LOGE("Create alarm thread failed\n");
237 }
238}