blob: 70b774572d90cb294ec449b5246665ffe26403f4 [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
Bart Van Assche5c604b92024-07-29 14:22:31 -070033#include <BpfSyscallWrappers.h>
Yifan Hong50c9e252019-10-03 16:26:13 -070034#include <health/utils.h>
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080035
Bart Van Assche5c604b92024-07-29 14:22:31 -070036using android::base::ErrnoError;
37using android::base::Result;
38using android::base::unique_fd;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080039using namespace android;
Yifan Hong50c9e252019-10-03 16:26:13 -070040using namespace std::chrono_literals;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080041
Yifan Hong50c9e252019-10-03 16:26:13 -070042namespace android {
43namespace hardware {
44namespace health {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080045
Yifan Hong50c9e252019-10-03 16:26:13 -070046HealthLoop::HealthLoop() {
47 InitHealthdConfig(&healthd_config_);
48 awake_poll_interval_ = -1;
49 wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
50}
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080051
Yifan Hong50c9e252019-10-03 16:26:13 -070052HealthLoop::~HealthLoop() {
53 LOG(FATAL) << "HealthLoop cannot be destroyed";
54}
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080055
Yifan Hong50c9e252019-10-03 16:26:13 -070056int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
57 CHECK(!reject_event_register_);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080058
Bart Van Assche6fe36182024-08-05 15:43:27 -070059 auto* event_handler = event_handlers_
60 .emplace_back(std::make_unique<EventHandler>(
61 EventHandler{this, fd, std::move(func)}))
62 .get();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080063
Bart Van Assche0cfcfac2024-08-05 15:51:46 -070064 struct epoll_event ev = {
65 .events = EPOLLIN | EPOLLERR,
66 .data.ptr = reinterpret_cast<void*>(event_handler),
67 };
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080068
69 if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
70
Yifan Hong50c9e252019-10-03 16:26:13 -070071 if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080072 KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
73 return -1;
74 }
75
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080076 return 0;
77}
78
Yifan Hong50c9e252019-10-03 16:26:13 -070079void HealthLoop::WakeAlarmSetInterval(int interval) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080080 struct itimerspec itval;
81
Yifan Hong50c9e252019-10-03 16:26:13 -070082 if (wakealarm_fd_ == -1) return;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080083
Yifan Hong50c9e252019-10-03 16:26:13 -070084 wakealarm_wake_interval_ = interval;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080085
86 if (interval == -1) interval = 0;
87
88 itval.it_interval.tv_sec = interval;
89 itval.it_interval.tv_nsec = 0;
90 itval.it_value.tv_sec = interval;
91 itval.it_value.tv_nsec = 0;
92
Yifan Hong50c9e252019-10-03 16:26:13 -070093 if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080094 KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
95}
96
Yifan Hong50c9e252019-10-03 16:26:13 -070097void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080098 // Fast wake interval when on charger (watch for overheat);
99 // slow wake interval when on battery (watch for drained battery).
100
Yifan Hong50c9e252019-10-03 16:26:13 -0700101 int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
102 : healthd_config_.periodic_chores_interval_slow;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800103
Yifan Hong50c9e252019-10-03 16:26:13 -0700104 if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800105
106 // During awake periods poll at fast rate. If wake alarm is set at fast
107 // rate then just use the alarm; if wake alarm is set at slow rate then
108 // poll at fast rate while awake and let alarm wake up at slow rate when
109 // asleep.
110
Yifan Hong50c9e252019-10-03 16:26:13 -0700111 if (healthd_config_.periodic_chores_interval_fast == -1)
112 awake_poll_interval_ = -1;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800113 else
Yifan Hong50c9e252019-10-03 16:26:13 -0700114 awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
115 ? -1
116 : healthd_config_.periodic_chores_interval_fast * 1000;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800117}
118
Yifan Hong50c9e252019-10-03 16:26:13 -0700119void HealthLoop::PeriodicChores() {
120 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800121}
122
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800123#define UEVENT_MSG_LEN 2048
Bart Van Assche0cfcfac2024-08-05 15:51:46 -0700124void HealthLoop::UeventEvent(uint32_t epevents) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700125 // No need to lock because uevent_fd_ is guaranteed to be initialized.
126
Bart Van Assche0cfcfac2024-08-05 15:51:46 -0700127 if (epevents & EPOLLERR) {
128 // The netlink receive buffer overflowed.
129 ScheduleBatteryUpdate();
130 return;
131 }
132
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800133 char msg[UEVENT_MSG_LEN + 2];
134 char* cp;
135 int n;
136
Yifan Hong50c9e252019-10-03 16:26:13 -0700137 n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800138 if (n <= 0) return;
139 if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
140 return;
141
142 msg[n] = '\0';
143 msg[n + 1] = '\0';
144 cp = msg;
145
146 while (*cp) {
Bart Van Asscheac9f9cb2021-10-19 11:55:53 -0700147 if (!strcmp(cp, "SUBSYSTEM=power_supply")) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700148 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800149 break;
150 }
151
152 /* advance to after the next \0 */
153 while (*cp++)
154 ;
155 }
156}
157
Bart Van Assche5c604b92024-07-29 14:22:31 -0700158// Attach a BPF filter to the @uevent_fd file descriptor. This fails in recovery mode because BPF is
159// not supported in recovery mode. This fails for kernel versions 5.4 and before because the BPF
160// program is rejected by the BPF verifier of older kernels.
161Result<void> HealthLoop::AttachFilter(int uevent_fd) {
162 static const char prg[] =
163 "/sys/fs/bpf/vendor/prog_filterPowerSupplyEvents_skfilter_power_supply";
164 int filter_fd(bpf::retrieveProgram(prg));
165 if (filter_fd < 0) {
166 return ErrnoError() << "failed to load BPF program " << prg;
167 }
168 if (setsockopt(uevent_fd, SOL_SOCKET, SO_ATTACH_BPF, &filter_fd, sizeof(filter_fd)) < 0) {
169 close(filter_fd);
170 return ErrnoError() << "failed to attach BPF program";
171 }
172 close(filter_fd);
173 return {};
174}
175
Yifan Hong50c9e252019-10-03 16:26:13 -0700176void HealthLoop::UeventInit(void) {
Bart Van Assche5c604b92024-07-29 14:22:31 -0700177 uevent_fd_.reset(uevent_create_socket(64 * 1024, true));
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800178
Yifan Hong50c9e252019-10-03 16:26:13 -0700179 if (uevent_fd_ < 0) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800180 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
181 return;
182 }
183
Yifan Hong50c9e252019-10-03 16:26:13 -0700184 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
Bart Van Assche5c604b92024-07-29 14:22:31 -0700185
186 Result<void> attach_result = AttachFilter(uevent_fd_);
187 if (!attach_result.ok()) {
188 std::string error_msg = attach_result.error().message();
189 error_msg +=
190 ". This is expected in recovery mode and also for kernel versions before 5.10.";
191 KLOG_WARNING(LOG_TAG, "%s", error_msg.c_str());
192 } else {
193 KLOG_INFO(LOG_TAG, "Successfully attached the BPF filter to the uevent socket");
194 }
195
Yifan Hong50c9e252019-10-03 16:26:13 -0700196 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800197 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700198
199 if (uevent_bind(uevent_fd_.get()) < 0) {
200 uevent_fd_.reset();
201 KLOG_ERROR(LOG_TAG, "uevent_init: binding socket failed\n");
202 return;
203 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800204}
205
Yifan Hong50c9e252019-10-03 16:26:13 -0700206void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
207 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
208
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800209 unsigned long long wakeups;
210
Yifan Hong50c9e252019-10-03 16:26:13 -0700211 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800212 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
213 return;
214 }
215
Yifan Hong50c9e252019-10-03 16:26:13 -0700216 PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800217}
218
Yifan Hong50c9e252019-10-03 16:26:13 -0700219void HealthLoop::WakeAlarmInit(void) {
220 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
221 if (wakealarm_fd_ == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800222 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
223 return;
224 }
225
Yifan Hong50c9e252019-10-03 16:26:13 -0700226 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800227 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
228
Yifan Hong50c9e252019-10-03 16:26:13 -0700229 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800230}
231
Yifan Hong50c9e252019-10-03 16:26:13 -0700232void HealthLoop::MainLoop(void) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800233 int nevents = 0;
234 while (1) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700235 reject_event_register_ = true;
236 size_t eventct = event_handlers_.size();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800237 struct epoll_event events[eventct];
Yifan Hong50c9e252019-10-03 16:26:13 -0700238 int timeout = awake_poll_interval_;
239
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800240 int mode_timeout;
241
242 /* Don't wait for first timer timeout to run periodic chores */
Yifan Hong50c9e252019-10-03 16:26:13 -0700243 if (!nevents) PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800244
Yifan Hong50c9e252019-10-03 16:26:13 -0700245 Heartbeat();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800246
Yifan Hong50c9e252019-10-03 16:26:13 -0700247 mode_timeout = PrepareToWait();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800248 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
Yifan Hong50c9e252019-10-03 16:26:13 -0700249 nevents = epoll_wait(epollfd_, events, eventct, timeout);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800250 if (nevents == -1) {
251 if (errno == EINTR) continue;
252 KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
253 break;
254 }
255
256 for (int n = 0; n < nevents; ++n) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700257 if (events[n].data.ptr) {
258 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
259 event_handler->func(event_handler->object, events[n].events);
260 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800261 }
262 }
263
264 return;
265}
266
Yifan Hong50c9e252019-10-03 16:26:13 -0700267int HealthLoop::InitInternal() {
268 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
269 if (epollfd_ == -1) {
Nick Kralevich8c038f22018-12-15 11:36:47 -0800270 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800271 return -1;
272 }
273
Yifan Hong50c9e252019-10-03 16:26:13 -0700274 // Call subclass's init for any additional init steps.
275 // Note that healthd_config_ is initialized before wakealarm_fd_; see
276 // AdjustUeventWakealarmPeriods().
277 Init(&healthd_config_);
278
279 WakeAlarmInit();
280 UeventInit();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800281
282 return 0;
283}
284
Yifan Hong50c9e252019-10-03 16:26:13 -0700285int HealthLoop::StartLoop() {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800286 int ret;
287
288 klog_set_level(KLOG_LEVEL);
289
Yifan Hong50c9e252019-10-03 16:26:13 -0700290 ret = InitInternal();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800291 if (ret) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700292 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
293 return 2;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800294 }
295
Yifan Hong50c9e252019-10-03 16:26:13 -0700296 MainLoop();
297 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800298 return 3;
299}
Yifan Hong50c9e252019-10-03 16:26:13 -0700300
301} // namespace health
302} // namespace hardware
303} // namespace android