blob: 691d8966e6dad2446d7a84e22090b7a8b1d9650d [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 Assche7b7948b2024-08-29 07:01:28 -070066 struct epoll_event ev = {
67 .events = EPOLLIN,
68 .data.ptr = reinterpret_cast<void*>(event_handler),
69 };
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080070
71 if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
72
Yifan Hong50c9e252019-10-03 16:26:13 -070073 if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080074 KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
75 return -1;
76 }
77
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080078 return 0;
79}
80
Yifan Hong50c9e252019-10-03 16:26:13 -070081void HealthLoop::WakeAlarmSetInterval(int interval) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080082 struct itimerspec itval;
83
Yifan Hong50c9e252019-10-03 16:26:13 -070084 if (wakealarm_fd_ == -1) return;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080085
Yifan Hong50c9e252019-10-03 16:26:13 -070086 wakealarm_wake_interval_ = interval;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080087
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
Yifan Hong50c9e252019-10-03 16:26:13 -070095 if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
Hridya Valsarajud3e3d722017-12-11 17:31:00 -080096 KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
97}
98
Yifan Hong50c9e252019-10-03 16:26:13 -070099void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800100 // Fast wake interval when on charger (watch for overheat);
101 // slow wake interval when on battery (watch for drained battery).
102
Yifan Hong50c9e252019-10-03 16:26:13 -0700103 int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
104 : healthd_config_.periodic_chores_interval_slow;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800105
Yifan Hong50c9e252019-10-03 16:26:13 -0700106 if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800107
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
Yifan Hong50c9e252019-10-03 16:26:13 -0700113 if (healthd_config_.periodic_chores_interval_fast == -1)
114 awake_poll_interval_ = -1;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800115 else
Yifan Hong50c9e252019-10-03 16:26:13 -0700116 awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
117 ? -1
118 : healthd_config_.periodic_chores_interval_fast * 1000;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800119}
120
Yifan Hong50c9e252019-10-03 16:26:13 -0700121void HealthLoop::PeriodicChores() {
122 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800123}
124
Bart Van Assche52646192024-08-29 12:40:50 -0700125// Returns true if and only if the battery statistics should be updated.
126bool HealthLoop::RecvUevents() {
127 bool update_stats = false;
128 for (;;) {
129 char msg[kUeventMsgLen + 2];
130 int n = uevent_kernel_multicast_recv(uevent_fd_, msg, kUeventMsgLen);
131 if (n <= 0) return update_stats;
132 if (n >= kUeventMsgLen) {
133 // too long -- discard
134 continue;
135 }
136 if (update_stats) {
137 continue;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800138 }
139
Bart Van Assche52646192024-08-29 12:40:50 -0700140 msg[n] = '\0';
141 msg[n + 1] = '\0';
142 for (char* cp = msg; *cp;) {
143 if (strcmp(cp, "SUBSYSTEM=power_supply") == 0) {
144 update_stats = true;
145 break;
146 }
147
148 /* advance to after the next \0 */
149 while (*cp++) {
150 }
151 }
152 }
153}
154
155void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
156 if (RecvUevents()) {
157 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800158 }
159}
160
Bart Van Assche5c604b92024-07-29 14:22:31 -0700161// Attach a BPF filter to the @uevent_fd file descriptor. This fails in recovery mode because BPF is
162// not supported in recovery mode. This fails for kernel versions 5.4 and before because the BPF
163// program is rejected by the BPF verifier of older kernels.
164Result<void> HealthLoop::AttachFilter(int uevent_fd) {
165 static const char prg[] =
166 "/sys/fs/bpf/vendor/prog_filterPowerSupplyEvents_skfilter_power_supply";
167 int filter_fd(bpf::retrieveProgram(prg));
168 if (filter_fd < 0) {
169 return ErrnoError() << "failed to load BPF program " << prg;
170 }
171 if (setsockopt(uevent_fd, SOL_SOCKET, SO_ATTACH_BPF, &filter_fd, sizeof(filter_fd)) < 0) {
172 close(filter_fd);
173 return ErrnoError() << "failed to attach BPF program";
174 }
175 close(filter_fd);
176 return {};
177}
178
Yifan Hong50c9e252019-10-03 16:26:13 -0700179void HealthLoop::UeventInit(void) {
Bart Van Assche5c604b92024-07-29 14:22:31 -0700180 uevent_fd_.reset(uevent_create_socket(64 * 1024, true));
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800181
Yifan Hong50c9e252019-10-03 16:26:13 -0700182 if (uevent_fd_ < 0) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800183 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
184 return;
185 }
186
Yifan Hong50c9e252019-10-03 16:26:13 -0700187 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
Bart Van Assche5c604b92024-07-29 14:22:31 -0700188
189 Result<void> attach_result = AttachFilter(uevent_fd_);
190 if (!attach_result.ok()) {
191 std::string error_msg = attach_result.error().message();
192 error_msg +=
193 ". This is expected in recovery mode and also for kernel versions before 5.10.";
Bart Van Assche1f2a9d02024-08-29 08:08:30 -0700194 KLOG_WARNING(LOG_TAG, "%s\n", error_msg.c_str());
Bart Van Assche5c604b92024-07-29 14:22:31 -0700195 } else {
Bart Van Assche1f2a9d02024-08-29 08:08:30 -0700196 KLOG_INFO(LOG_TAG, "Successfully attached the BPF filter to the uevent socket\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700197 }
198
Yifan Hong50c9e252019-10-03 16:26:13 -0700199 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800200 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700201
202 if (uevent_bind(uevent_fd_.get()) < 0) {
203 uevent_fd_.reset();
204 KLOG_ERROR(LOG_TAG, "uevent_init: binding socket failed\n");
205 return;
206 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800207}
208
Yifan Hong50c9e252019-10-03 16:26:13 -0700209void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
210 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
211
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800212 unsigned long long wakeups;
213
Yifan Hong50c9e252019-10-03 16:26:13 -0700214 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800215 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
216 return;
217 }
218
Yifan Hong50c9e252019-10-03 16:26:13 -0700219 PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800220}
221
Yifan Hong50c9e252019-10-03 16:26:13 -0700222void HealthLoop::WakeAlarmInit(void) {
223 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
224 if (wakealarm_fd_ == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800225 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
226 return;
227 }
228
Yifan Hong50c9e252019-10-03 16:26:13 -0700229 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800230 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
231
Yifan Hong50c9e252019-10-03 16:26:13 -0700232 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800233}
234
Yifan Hong50c9e252019-10-03 16:26:13 -0700235void HealthLoop::MainLoop(void) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800236 int nevents = 0;
237 while (1) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700238 reject_event_register_ = true;
239 size_t eventct = event_handlers_.size();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800240 struct epoll_event events[eventct];
Yifan Hong50c9e252019-10-03 16:26:13 -0700241 int timeout = awake_poll_interval_;
242
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800243 int mode_timeout;
244
245 /* Don't wait for first timer timeout to run periodic chores */
Yifan Hong50c9e252019-10-03 16:26:13 -0700246 if (!nevents) PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800247
Yifan Hong50c9e252019-10-03 16:26:13 -0700248 Heartbeat();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800249
Yifan Hong50c9e252019-10-03 16:26:13 -0700250 mode_timeout = PrepareToWait();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800251 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
Yifan Hong50c9e252019-10-03 16:26:13 -0700252 nevents = epoll_wait(epollfd_, events, eventct, timeout);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800253 if (nevents == -1) {
254 if (errno == EINTR) continue;
255 KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
256 break;
257 }
258
259 for (int n = 0; n < nevents; ++n) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700260 if (events[n].data.ptr) {
261 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
262 event_handler->func(event_handler->object, events[n].events);
263 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800264 }
265 }
266
267 return;
268}
269
Yifan Hong50c9e252019-10-03 16:26:13 -0700270int HealthLoop::InitInternal() {
271 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
272 if (epollfd_ == -1) {
Nick Kralevich8c038f22018-12-15 11:36:47 -0800273 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800274 return -1;
275 }
276
Yifan Hong50c9e252019-10-03 16:26:13 -0700277 // Call subclass's init for any additional init steps.
278 // Note that healthd_config_ is initialized before wakealarm_fd_; see
279 // AdjustUeventWakealarmPeriods().
280 Init(&healthd_config_);
281
282 WakeAlarmInit();
283 UeventInit();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800284
285 return 0;
286}
287
Yifan Hong50c9e252019-10-03 16:26:13 -0700288int HealthLoop::StartLoop() {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800289 int ret;
290
291 klog_set_level(KLOG_LEVEL);
292
Yifan Hong50c9e252019-10-03 16:26:13 -0700293 ret = InitInternal();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800294 if (ret) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700295 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
296 return 2;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800297 }
298
Yifan Hong50c9e252019-10-03 16:26:13 -0700299 MainLoop();
300 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800301 return 3;
302}
Yifan Hong50c9e252019-10-03 16:26:13 -0700303
304} // namespace health
305} // namespace hardware
306} // namespace android