blob: 12c8b349f63a0c0c7acd787385ce048cb1dc6a0c [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
Bart Van Assche52646192024-08-29 12:40:50 -070046static constexpr uint32_t kUeventMsgLen = 2048;
47
Yifan Hong50c9e252019-10-03 16:26:13 -070048HealthLoop::HealthLoop() {
49 InitHealthdConfig(&healthd_config_);
50 awake_poll_interval_ = -1;
51 wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
52}
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080053
Yifan Hong50c9e252019-10-03 16:26:13 -070054HealthLoop::~HealthLoop() {
55 LOG(FATAL) << "HealthLoop cannot be destroyed";
56}
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080057
Yifan Hong50c9e252019-10-03 16:26:13 -070058int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
59 CHECK(!reject_event_register_);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080060
Bart Van Assche6fe36182024-08-05 15:43:27 -070061 auto* event_handler = event_handlers_
62 .emplace_back(std::make_unique<EventHandler>(
63 EventHandler{this, fd, std::move(func)}))
64 .get();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080065
Bart Van Assche185589b2024-08-27 10:32:47 +000066 struct epoll_event ev;
67
68 ev.events = EPOLLIN;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080069
70 if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
71
Bart Van Assche185589b2024-08-27 10:32:47 +000072 ev.data.ptr = reinterpret_cast<void*>(event_handler);
73
Yifan Hong50c9e252019-10-03 16:26:13 -070074 if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080075 KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
76 return -1;
77 }
78
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080079 return 0;
80}
81
Yifan Hong50c9e252019-10-03 16:26:13 -070082void HealthLoop::WakeAlarmSetInterval(int interval) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080083 struct itimerspec itval;
84
Yifan Hong50c9e252019-10-03 16:26:13 -070085 if (wakealarm_fd_ == -1) return;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080086
Yifan Hong50c9e252019-10-03 16:26:13 -070087 wakealarm_wake_interval_ = interval;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080088
89 if (interval == -1) interval = 0;
90
91 itval.it_interval.tv_sec = interval;
92 itval.it_interval.tv_nsec = 0;
93 itval.it_value.tv_sec = interval;
94 itval.it_value.tv_nsec = 0;
95
Yifan Hong50c9e252019-10-03 16:26:13 -070096 if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080097 KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
98}
99
Yifan Hong50c9e252019-10-03 16:26:13 -0700100void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800101 // Fast wake interval when on charger (watch for overheat);
102 // slow wake interval when on battery (watch for drained battery).
103
Yifan Hong50c9e252019-10-03 16:26:13 -0700104 int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
105 : healthd_config_.periodic_chores_interval_slow;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800106
Yifan Hong50c9e252019-10-03 16:26:13 -0700107 if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800108
109 // During awake periods poll at fast rate. If wake alarm is set at fast
110 // rate then just use the alarm; if wake alarm is set at slow rate then
111 // poll at fast rate while awake and let alarm wake up at slow rate when
112 // asleep.
113
Yifan Hong50c9e252019-10-03 16:26:13 -0700114 if (healthd_config_.periodic_chores_interval_fast == -1)
115 awake_poll_interval_ = -1;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800116 else
Yifan Hong50c9e252019-10-03 16:26:13 -0700117 awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
118 ? -1
119 : healthd_config_.periodic_chores_interval_fast * 1000;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800120}
121
Yifan Hong50c9e252019-10-03 16:26:13 -0700122void HealthLoop::PeriodicChores() {
123 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800124}
125
Bart Van Assche52646192024-08-29 12:40:50 -0700126// Returns true if and only if the battery statistics should be updated.
127bool HealthLoop::RecvUevents() {
128 bool update_stats = false;
129 for (;;) {
130 char msg[kUeventMsgLen + 2];
131 int n = uevent_kernel_multicast_recv(uevent_fd_, msg, kUeventMsgLen);
132 if (n <= 0) return update_stats;
133 if (n >= kUeventMsgLen) {
134 // too long -- discard
135 continue;
136 }
137 if (update_stats) {
138 continue;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800139 }
140
Bart Van Assche52646192024-08-29 12:40:50 -0700141 msg[n] = '\0';
142 msg[n + 1] = '\0';
143 for (char* cp = msg; *cp;) {
144 if (strcmp(cp, "SUBSYSTEM=power_supply") == 0) {
145 update_stats = true;
146 break;
147 }
148
149 /* advance to after the next \0 */
150 while (*cp++) {
151 }
152 }
153 }
154}
155
156void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
157 if (RecvUevents()) {
158 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800159 }
160}
161
Bart Van Assche5c604b92024-07-29 14:22:31 -0700162// Attach a BPF filter to the @uevent_fd file descriptor. This fails in recovery mode because BPF is
163// not supported in recovery mode. This fails for kernel versions 5.4 and before because the BPF
164// program is rejected by the BPF verifier of older kernels.
165Result<void> HealthLoop::AttachFilter(int uevent_fd) {
166 static const char prg[] =
167 "/sys/fs/bpf/vendor/prog_filterPowerSupplyEvents_skfilter_power_supply";
168 int filter_fd(bpf::retrieveProgram(prg));
169 if (filter_fd < 0) {
170 return ErrnoError() << "failed to load BPF program " << prg;
171 }
172 if (setsockopt(uevent_fd, SOL_SOCKET, SO_ATTACH_BPF, &filter_fd, sizeof(filter_fd)) < 0) {
173 close(filter_fd);
174 return ErrnoError() << "failed to attach BPF program";
175 }
176 close(filter_fd);
177 return {};
178}
179
Yifan Hong50c9e252019-10-03 16:26:13 -0700180void HealthLoop::UeventInit(void) {
Bart Van Assche5c604b92024-07-29 14:22:31 -0700181 uevent_fd_.reset(uevent_create_socket(64 * 1024, true));
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800182
Yifan Hong50c9e252019-10-03 16:26:13 -0700183 if (uevent_fd_ < 0) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800184 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
185 return;
186 }
187
Yifan Hong50c9e252019-10-03 16:26:13 -0700188 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
Bart Van Assche5c604b92024-07-29 14:22:31 -0700189
190 Result<void> attach_result = AttachFilter(uevent_fd_);
191 if (!attach_result.ok()) {
192 std::string error_msg = attach_result.error().message();
193 error_msg +=
194 ". This is expected in recovery mode and also for kernel versions before 5.10.";
Bart Van Assche1f2a9d02024-08-29 08:08:30 -0700195 KLOG_WARNING(LOG_TAG, "%s\n", error_msg.c_str());
Bart Van Assche5c604b92024-07-29 14:22:31 -0700196 } else {
Bart Van Assche1f2a9d02024-08-29 08:08:30 -0700197 KLOG_INFO(LOG_TAG, "Successfully attached the BPF filter to the uevent socket\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700198 }
199
Yifan Hong50c9e252019-10-03 16:26:13 -0700200 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800201 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700202
203 if (uevent_bind(uevent_fd_.get()) < 0) {
204 uevent_fd_.reset();
205 KLOG_ERROR(LOG_TAG, "uevent_init: binding socket failed\n");
206 return;
207 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800208}
209
Yifan Hong50c9e252019-10-03 16:26:13 -0700210void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
211 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
212
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800213 unsigned long long wakeups;
214
Yifan Hong50c9e252019-10-03 16:26:13 -0700215 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800216 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
217 return;
218 }
219
Yifan Hong50c9e252019-10-03 16:26:13 -0700220 PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800221}
222
Yifan Hong50c9e252019-10-03 16:26:13 -0700223void HealthLoop::WakeAlarmInit(void) {
224 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
225 if (wakealarm_fd_ == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800226 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
227 return;
228 }
229
Yifan Hong50c9e252019-10-03 16:26:13 -0700230 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800231 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
232
Yifan Hong50c9e252019-10-03 16:26:13 -0700233 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800234}
235
Yifan Hong50c9e252019-10-03 16:26:13 -0700236void HealthLoop::MainLoop(void) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800237 int nevents = 0;
238 while (1) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700239 reject_event_register_ = true;
240 size_t eventct = event_handlers_.size();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800241 struct epoll_event events[eventct];
Yifan Hong50c9e252019-10-03 16:26:13 -0700242 int timeout = awake_poll_interval_;
243
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800244 int mode_timeout;
245
246 /* Don't wait for first timer timeout to run periodic chores */
Yifan Hong50c9e252019-10-03 16:26:13 -0700247 if (!nevents) PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800248
Yifan Hong50c9e252019-10-03 16:26:13 -0700249 Heartbeat();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800250
Yifan Hong50c9e252019-10-03 16:26:13 -0700251 mode_timeout = PrepareToWait();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800252 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
Yifan Hong50c9e252019-10-03 16:26:13 -0700253 nevents = epoll_wait(epollfd_, events, eventct, timeout);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800254 if (nevents == -1) {
255 if (errno == EINTR) continue;
256 KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
257 break;
258 }
259
260 for (int n = 0; n < nevents; ++n) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700261 if (events[n].data.ptr) {
262 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
263 event_handler->func(event_handler->object, events[n].events);
264 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800265 }
266 }
267
268 return;
269}
270
Yifan Hong50c9e252019-10-03 16:26:13 -0700271int HealthLoop::InitInternal() {
272 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
273 if (epollfd_ == -1) {
Nick Kralevich8c038f22018-12-15 11:36:47 -0800274 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800275 return -1;
276 }
277
Yifan Hong50c9e252019-10-03 16:26:13 -0700278 // Call subclass's init for any additional init steps.
279 // Note that healthd_config_ is initialized before wakealarm_fd_; see
280 // AdjustUeventWakealarmPeriods().
281 Init(&healthd_config_);
282
283 WakeAlarmInit();
284 UeventInit();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800285
286 return 0;
287}
288
Yifan Hong50c9e252019-10-03 16:26:13 -0700289int HealthLoop::StartLoop() {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800290 int ret;
291
292 klog_set_level(KLOG_LEVEL);
293
Yifan Hong50c9e252019-10-03 16:26:13 -0700294 ret = InitInternal();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800295 if (ret) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700296 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
297 return 2;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800298 }
299
Yifan Hong50c9e252019-10-03 16:26:13 -0700300 MainLoop();
301 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800302 return 3;
303}
Yifan Hong50c9e252019-10-03 16:26:13 -0700304
305} // namespace health
306} // namespace hardware
307} // namespace android