blob: f8c3490cbeecbd143678c672749937672fe9bbf4 [file] [log] [blame]
Hridya Valsarajud3e3d722017-12-11 17:31:00 -08001/*
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
Yifan Hong50c9e252019-10-03 16:26:13 -070017#define LOG_TAG "HealthLoop"
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080018#define KLOG_LEVEL 6
19
Yifan Hong50c9e252019-10-03 16:26:13 -070020#include <health/HealthLoop.h>
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080021
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080022#include <errno.h>
Bart Van Assche70878582024-08-04 07:14:29 -070023#include <sys/epoll.h> // epoll_create1(), epoll_ctl(), epoll_wait()
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080024#include <sys/timerfd.h>
Bart Van Assche70878582024-08-04 07:14:29 -070025#include <unistd.h> // read()
Yifan Hong50c9e252019-10-03 16:26:13 -070026
27#include <android-base/logging.h>
28#include <batteryservice/BatteryService.h>
Bart Van Assche70878582024-08-04 07:14:29 -070029#include <cutils/klog.h> // KLOG_*()
Yifan Hong50c9e252019-10-03 16:26:13 -070030#include <cutils/uevent.h>
31#include <healthd/healthd.h>
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080032
Yifan Hong50c9e252019-10-03 16:26:13 -070033#include <health/utils.h>
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080034
35using namespace android;
Yifan Hong50c9e252019-10-03 16:26:13 -070036using namespace std::chrono_literals;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080037
Yifan Hong50c9e252019-10-03 16:26:13 -070038namespace android {
39namespace hardware {
40namespace health {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080041
Yifan Hong50c9e252019-10-03 16:26:13 -070042HealthLoop::HealthLoop() {
43 InitHealthdConfig(&healthd_config_);
44 awake_poll_interval_ = -1;
45 wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
46}
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080047
Yifan Hong50c9e252019-10-03 16:26:13 -070048HealthLoop::~HealthLoop() {
49 LOG(FATAL) << "HealthLoop cannot be destroyed";
50}
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080051
Yifan Hong50c9e252019-10-03 16:26:13 -070052int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
53 CHECK(!reject_event_register_);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080054
Bart Van Assche6fe36182024-08-05 15:43:27 -070055 auto* event_handler = event_handlers_
56 .emplace_back(std::make_unique<EventHandler>(
57 EventHandler{this, fd, std::move(func)}))
58 .get();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080059
Bart Van Assche0cfcfac2024-08-05 15:51:46 -070060 struct epoll_event ev = {
61 .events = EPOLLIN | EPOLLERR,
62 .data.ptr = reinterpret_cast<void*>(event_handler),
63 };
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080064
65 if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
66
Yifan Hong50c9e252019-10-03 16:26:13 -070067 if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080068 KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
69 return -1;
70 }
71
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080072 return 0;
73}
74
Yifan Hong50c9e252019-10-03 16:26:13 -070075void HealthLoop::WakeAlarmSetInterval(int interval) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080076 struct itimerspec itval;
77
Yifan Hong50c9e252019-10-03 16:26:13 -070078 if (wakealarm_fd_ == -1) return;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080079
Yifan Hong50c9e252019-10-03 16:26:13 -070080 wakealarm_wake_interval_ = interval;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080081
82 if (interval == -1) interval = 0;
83
84 itval.it_interval.tv_sec = interval;
85 itval.it_interval.tv_nsec = 0;
86 itval.it_value.tv_sec = interval;
87 itval.it_value.tv_nsec = 0;
88
Yifan Hong50c9e252019-10-03 16:26:13 -070089 if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080090 KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
91}
92
Yifan Hong50c9e252019-10-03 16:26:13 -070093void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080094 // Fast wake interval when on charger (watch for overheat);
95 // slow wake interval when on battery (watch for drained battery).
96
Yifan Hong50c9e252019-10-03 16:26:13 -070097 int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
98 : healthd_config_.periodic_chores_interval_slow;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080099
Yifan Hong50c9e252019-10-03 16:26:13 -0700100 if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800101
102 // During awake periods poll at fast rate. If wake alarm is set at fast
103 // rate then just use the alarm; if wake alarm is set at slow rate then
104 // poll at fast rate while awake and let alarm wake up at slow rate when
105 // asleep.
106
Yifan Hong50c9e252019-10-03 16:26:13 -0700107 if (healthd_config_.periodic_chores_interval_fast == -1)
108 awake_poll_interval_ = -1;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800109 else
Yifan Hong50c9e252019-10-03 16:26:13 -0700110 awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
111 ? -1
112 : healthd_config_.periodic_chores_interval_fast * 1000;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800113}
114
Yifan Hong50c9e252019-10-03 16:26:13 -0700115void HealthLoop::PeriodicChores() {
116 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800117}
118
Yifan Hong50c9e252019-10-03 16:26:13 -0700119// TODO(b/140330870): Use BPF instead.
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800120#define UEVENT_MSG_LEN 2048
Bart Van Assche0cfcfac2024-08-05 15:51:46 -0700121void HealthLoop::UeventEvent(uint32_t epevents) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700122 // No need to lock because uevent_fd_ is guaranteed to be initialized.
123
Bart Van Assche0cfcfac2024-08-05 15:51:46 -0700124 if (epevents & EPOLLERR) {
125 // The netlink receive buffer overflowed.
126 ScheduleBatteryUpdate();
127 return;
128 }
129
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800130 char msg[UEVENT_MSG_LEN + 2];
131 char* cp;
132 int n;
133
Yifan Hong50c9e252019-10-03 16:26:13 -0700134 n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800135 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) {
Bart Van Asscheac9f9cb2021-10-19 11:55:53 -0700144 if (!strcmp(cp, "SUBSYSTEM=power_supply")) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700145 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800146 break;
147 }
148
149 /* advance to after the next \0 */
150 while (*cp++)
151 ;
152 }
153}
154
Yifan Hong50c9e252019-10-03 16:26:13 -0700155void HealthLoop::UeventInit(void) {
156 uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800157
Yifan Hong50c9e252019-10-03 16:26:13 -0700158 if (uevent_fd_ < 0) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800159 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
160 return;
161 }
162
Yifan Hong50c9e252019-10-03 16:26:13 -0700163 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
164 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800165 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
166}
167
Yifan Hong50c9e252019-10-03 16:26:13 -0700168void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
169 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
170
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800171 unsigned long long wakeups;
172
Yifan Hong50c9e252019-10-03 16:26:13 -0700173 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800174 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
175 return;
176 }
177
Yifan Hong50c9e252019-10-03 16:26:13 -0700178 PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800179}
180
Yifan Hong50c9e252019-10-03 16:26:13 -0700181void HealthLoop::WakeAlarmInit(void) {
182 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
183 if (wakealarm_fd_ == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800184 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
185 return;
186 }
187
Yifan Hong50c9e252019-10-03 16:26:13 -0700188 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800189 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
190
Yifan Hong50c9e252019-10-03 16:26:13 -0700191 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800192}
193
Yifan Hong50c9e252019-10-03 16:26:13 -0700194void HealthLoop::MainLoop(void) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800195 int nevents = 0;
196 while (1) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700197 reject_event_register_ = true;
198 size_t eventct = event_handlers_.size();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800199 struct epoll_event events[eventct];
Yifan Hong50c9e252019-10-03 16:26:13 -0700200 int timeout = awake_poll_interval_;
201
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800202 int mode_timeout;
203
204 /* Don't wait for first timer timeout to run periodic chores */
Yifan Hong50c9e252019-10-03 16:26:13 -0700205 if (!nevents) PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800206
Yifan Hong50c9e252019-10-03 16:26:13 -0700207 Heartbeat();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800208
Yifan Hong50c9e252019-10-03 16:26:13 -0700209 mode_timeout = PrepareToWait();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800210 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
Yifan Hong50c9e252019-10-03 16:26:13 -0700211 nevents = epoll_wait(epollfd_, events, eventct, timeout);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800212 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) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700219 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 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800223 }
224 }
225
226 return;
227}
228
Yifan Hong50c9e252019-10-03 16:26:13 -0700229int HealthLoop::InitInternal() {
230 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
231 if (epollfd_ == -1) {
Nick Kralevich8c038f22018-12-15 11:36:47 -0800232 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800233 return -1;
234 }
235
Yifan Hong50c9e252019-10-03 16:26:13 -0700236 // 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();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800243
244 return 0;
245}
246
Yifan Hong50c9e252019-10-03 16:26:13 -0700247int HealthLoop::StartLoop() {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800248 int ret;
249
250 klog_set_level(KLOG_LEVEL);
251
Yifan Hong50c9e252019-10-03 16:26:13 -0700252 ret = InitInternal();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800253 if (ret) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700254 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
255 return 2;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800256 }
257
Yifan Hong50c9e252019-10-03 16:26:13 -0700258 MainLoop();
259 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800260 return 3;
261}
Yifan Hong50c9e252019-10-03 16:26:13 -0700262
263} // namespace health
264} // namespace hardware
265} // namespace android