1 /*
2 * Copyright (C) 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 #define LOG_TAG "HealthLoop"
18 #define KLOG_LEVEL 6
19
20 #include <health/HealthLoop.h>
21
22 #include <errno.h>
23 #include <libgen.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/epoll.h>
28 #include <sys/timerfd.h>
29 #include <unistd.h>
30
31 #include <android-base/logging.h>
32 #include <batteryservice/BatteryService.h>
33 #include <cutils/klog.h>
34 #include <cutils/uevent.h>
35 #include <healthd/healthd.h>
36 #include <utils/Errors.h>
37
38 #include <health/utils.h>
39
40 using namespace android;
41 using namespace std::chrono_literals;
42
43 namespace android {
44 namespace hardware {
45 namespace health {
46
HealthLoop()47 HealthLoop::HealthLoop() {
48 InitHealthdConfig(&healthd_config_);
49 awake_poll_interval_ = -1;
50 wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
51 }
52
~HealthLoop()53 HealthLoop::~HealthLoop() {
54 LOG(FATAL) << "HealthLoop cannot be destroyed";
55 }
56
RegisterEvent(int fd,BoundFunction func,EventWakeup wakeup)57 int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
58 CHECK(!reject_event_register_);
59
60 auto* event_handler =
61 event_handlers_
62 .emplace_back(std::make_unique<EventHandler>(EventHandler{this, fd, func}))
63 .get();
64
65 struct epoll_event ev;
66
67 ev.events = EPOLLIN;
68
69 if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
70
71 ev.data.ptr = reinterpret_cast<void*>(event_handler);
72
73 if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
74 KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
75 return -1;
76 }
77
78 return 0;
79 }
80
WakeAlarmSetInterval(int interval)81 void HealthLoop::WakeAlarmSetInterval(int interval) {
82 struct itimerspec itval;
83
84 if (wakealarm_fd_ == -1) return;
85
86 wakealarm_wake_interval_ = interval;
87
88 if (interval == -1) interval = 0;
89
90 itval.it_interval.tv_sec = interval;
91 itval.it_interval.tv_nsec = 0;
92 itval.it_value.tv_sec = interval;
93 itval.it_value.tv_nsec = 0;
94
95 if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
96 KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
97 }
98
AdjustWakealarmPeriods(bool charger_online)99 void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
100 // Fast wake interval when on charger (watch for overheat);
101 // slow wake interval when on battery (watch for drained battery).
102
103 int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
104 : healthd_config_.periodic_chores_interval_slow;
105
106 if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
107
108 // During awake periods poll at fast rate. If wake alarm is set at fast
109 // rate then just use the alarm; if wake alarm is set at slow rate then
110 // poll at fast rate while awake and let alarm wake up at slow rate when
111 // asleep.
112
113 if (healthd_config_.periodic_chores_interval_fast == -1)
114 awake_poll_interval_ = -1;
115 else
116 awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
117 ? -1
118 : healthd_config_.periodic_chores_interval_fast * 1000;
119 }
120
PeriodicChores()121 void HealthLoop::PeriodicChores() {
122 ScheduleBatteryUpdate();
123 }
124
125 // TODO(b/140330870): Use BPF instead.
126 #define UEVENT_MSG_LEN 2048
UeventEvent(uint32_t)127 void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
128 // No need to lock because uevent_fd_ is guaranteed to be initialized.
129
130 char msg[UEVENT_MSG_LEN + 2];
131 char* cp;
132 int n;
133
134 n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
135 if (n <= 0) return;
136 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
137 return;
138
139 msg[n] = '\0';
140 msg[n + 1] = '\0';
141 cp = msg;
142
143 while (*cp) {
144 if (!strcmp(cp, "SUBSYSTEM=power_supply")) {
145 ScheduleBatteryUpdate();
146 break;
147 }
148
149 /* advance to after the next \0 */
150 while (*cp++)
151 ;
152 }
153 }
154
UeventInit(void)155 void HealthLoop::UeventInit(void) {
156 uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
157
158 if (uevent_fd_ < 0) {
159 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
160 return;
161 }
162
163 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
164 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
165 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
166 }
167
WakeAlarmEvent(uint32_t)168 void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
169 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
170
171 unsigned long long wakeups;
172
173 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
174 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
175 return;
176 }
177
178 PeriodicChores();
179 }
180
WakeAlarmInit(void)181 void HealthLoop::WakeAlarmInit(void) {
182 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
183 if (wakealarm_fd_ == -1) {
184 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
185 return;
186 }
187
188 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
189 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
190
191 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
192 }
193
MainLoop(void)194 void HealthLoop::MainLoop(void) {
195 int nevents = 0;
196 while (1) {
197 reject_event_register_ = true;
198 size_t eventct = event_handlers_.size();
199 struct epoll_event events[eventct];
200 int timeout = awake_poll_interval_;
201
202 int mode_timeout;
203
204 /* Don't wait for first timer timeout to run periodic chores */
205 if (!nevents) PeriodicChores();
206
207 Heartbeat();
208
209 mode_timeout = PrepareToWait();
210 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
211 nevents = epoll_wait(epollfd_, events, eventct, timeout);
212 if (nevents == -1) {
213 if (errno == EINTR) continue;
214 KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
215 break;
216 }
217
218 for (int n = 0; n < nevents; ++n) {
219 if (events[n].data.ptr) {
220 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
221 event_handler->func(event_handler->object, events[n].events);
222 }
223 }
224 }
225
226 return;
227 }
228
InitInternal()229 int HealthLoop::InitInternal() {
230 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
231 if (epollfd_ == -1) {
232 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
233 return -1;
234 }
235
236 // Call subclass's init for any additional init steps.
237 // Note that healthd_config_ is initialized before wakealarm_fd_; see
238 // AdjustUeventWakealarmPeriods().
239 Init(&healthd_config_);
240
241 WakeAlarmInit();
242 UeventInit();
243
244 return 0;
245 }
246
StartLoop()247 int HealthLoop::StartLoop() {
248 int ret;
249
250 klog_set_level(KLOG_LEVEL);
251
252 ret = InitInternal();
253 if (ret) {
254 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
255 return 2;
256 }
257
258 MainLoop();
259 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
260 return 3;
261 }
262
263 } // namespace health
264 } // namespace hardware
265 } // namespace android
266