blob: a43a69932cefa879571bf27fa41e73a80939053d [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 = {
Bart Van Assche32a9bfe2024-08-29 08:05:10 -070067 .events = EPOLLIN | EPOLLERR,
Bart Van Assche7b7948b2024-08-29 07:01:28 -070068 .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);
Bart Van Assche32a9bfe2024-08-29 08:05:10 -0700131 if (n < 0 && errno == ENOBUFS) {
132 update_stats = true;
133 }
Bart Van Assche52646192024-08-29 12:40:50 -0700134 if (n <= 0) return update_stats;
135 if (n >= kUeventMsgLen) {
136 // too long -- discard
137 continue;
138 }
139 if (update_stats) {
140 continue;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800141 }
142
Bart Van Assche52646192024-08-29 12:40:50 -0700143 msg[n] = '\0';
144 msg[n + 1] = '\0';
145 for (char* cp = msg; *cp;) {
146 if (strcmp(cp, "SUBSYSTEM=power_supply") == 0) {
147 update_stats = true;
148 break;
149 }
150
151 /* advance to after the next \0 */
152 while (*cp++) {
153 }
154 }
155 }
156}
157
158void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
159 if (RecvUevents()) {
160 ScheduleBatteryUpdate();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800161 }
162}
163
Bart Van Assche5c604b92024-07-29 14:22:31 -0700164// Attach a BPF filter to the @uevent_fd file descriptor. This fails in recovery mode because BPF is
165// not supported in recovery mode. This fails for kernel versions 5.4 and before because the BPF
166// program is rejected by the BPF verifier of older kernels.
167Result<void> HealthLoop::AttachFilter(int uevent_fd) {
168 static const char prg[] =
169 "/sys/fs/bpf/vendor/prog_filterPowerSupplyEvents_skfilter_power_supply";
170 int filter_fd(bpf::retrieveProgram(prg));
171 if (filter_fd < 0) {
172 return ErrnoError() << "failed to load BPF program " << prg;
173 }
174 if (setsockopt(uevent_fd, SOL_SOCKET, SO_ATTACH_BPF, &filter_fd, sizeof(filter_fd)) < 0) {
175 close(filter_fd);
176 return ErrnoError() << "failed to attach BPF program";
177 }
178 close(filter_fd);
179 return {};
180}
181
Yifan Hong50c9e252019-10-03 16:26:13 -0700182void HealthLoop::UeventInit(void) {
Bart Van Assche5c604b92024-07-29 14:22:31 -0700183 uevent_fd_.reset(uevent_create_socket(64 * 1024, true));
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800184
Yifan Hong50c9e252019-10-03 16:26:13 -0700185 if (uevent_fd_ < 0) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800186 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
187 return;
188 }
189
Yifan Hong50c9e252019-10-03 16:26:13 -0700190 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
Bart Van Assche5c604b92024-07-29 14:22:31 -0700191
192 Result<void> attach_result = AttachFilter(uevent_fd_);
193 if (!attach_result.ok()) {
194 std::string error_msg = attach_result.error().message();
195 error_msg +=
196 ". This is expected in recovery mode and also for kernel versions before 5.10.";
Bart Van Assche1f2a9d02024-08-29 08:08:30 -0700197 KLOG_WARNING(LOG_TAG, "%s\n", error_msg.c_str());
Bart Van Assche5c604b92024-07-29 14:22:31 -0700198 } else {
Bart Van Assche1f2a9d02024-08-29 08:08:30 -0700199 KLOG_INFO(LOG_TAG, "Successfully attached the BPF filter to the uevent socket\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700200 }
201
Yifan Hong50c9e252019-10-03 16:26:13 -0700202 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800203 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
Bart Van Assche5c604b92024-07-29 14:22:31 -0700204
205 if (uevent_bind(uevent_fd_.get()) < 0) {
206 uevent_fd_.reset();
207 KLOG_ERROR(LOG_TAG, "uevent_init: binding socket failed\n");
208 return;
209 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800210}
211
Yifan Hong50c9e252019-10-03 16:26:13 -0700212void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
213 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
214
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800215 unsigned long long wakeups;
216
Yifan Hong50c9e252019-10-03 16:26:13 -0700217 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800218 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
219 return;
220 }
221
Yifan Hong50c9e252019-10-03 16:26:13 -0700222 PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800223}
224
Yifan Hong50c9e252019-10-03 16:26:13 -0700225void HealthLoop::WakeAlarmInit(void) {
226 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
227 if (wakealarm_fd_ == -1) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800228 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
229 return;
230 }
231
Yifan Hong50c9e252019-10-03 16:26:13 -0700232 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800233 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
234
Yifan Hong50c9e252019-10-03 16:26:13 -0700235 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800236}
237
Yifan Hong50c9e252019-10-03 16:26:13 -0700238void HealthLoop::MainLoop(void) {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800239 int nevents = 0;
240 while (1) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700241 reject_event_register_ = true;
242 size_t eventct = event_handlers_.size();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800243 struct epoll_event events[eventct];
Yifan Hong50c9e252019-10-03 16:26:13 -0700244 int timeout = awake_poll_interval_;
245
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800246 int mode_timeout;
247
248 /* Don't wait for first timer timeout to run periodic chores */
Yifan Hong50c9e252019-10-03 16:26:13 -0700249 if (!nevents) PeriodicChores();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800250
Yifan Hong50c9e252019-10-03 16:26:13 -0700251 Heartbeat();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800252
Yifan Hong50c9e252019-10-03 16:26:13 -0700253 mode_timeout = PrepareToWait();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800254 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
Yifan Hong50c9e252019-10-03 16:26:13 -0700255 nevents = epoll_wait(epollfd_, events, eventct, timeout);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800256 if (nevents == -1) {
257 if (errno == EINTR) continue;
258 KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
259 break;
260 }
261
262 for (int n = 0; n < nevents; ++n) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700263 if (events[n].data.ptr) {
264 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
265 event_handler->func(event_handler->object, events[n].events);
266 }
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800267 }
268 }
269
270 return;
271}
272
Yifan Hong50c9e252019-10-03 16:26:13 -0700273int HealthLoop::InitInternal() {
274 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
275 if (epollfd_ == -1) {
Nick Kralevich8c038f22018-12-15 11:36:47 -0800276 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800277 return -1;
278 }
279
Yifan Hong50c9e252019-10-03 16:26:13 -0700280 // Call subclass's init for any additional init steps.
281 // Note that healthd_config_ is initialized before wakealarm_fd_; see
282 // AdjustUeventWakealarmPeriods().
283 Init(&healthd_config_);
284
285 WakeAlarmInit();
286 UeventInit();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800287
288 return 0;
289}
290
Yifan Hong50c9e252019-10-03 16:26:13 -0700291int HealthLoop::StartLoop() {
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800292 int ret;
293
294 klog_set_level(KLOG_LEVEL);
295
Yifan Hong50c9e252019-10-03 16:26:13 -0700296 ret = InitInternal();
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800297 if (ret) {
Yifan Hong50c9e252019-10-03 16:26:13 -0700298 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
299 return 2;
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800300 }
301
Yifan Hong50c9e252019-10-03 16:26:13 -0700302 MainLoop();
303 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
Hridya Valsarajud3e3d722017-12-11 17:31:00 -0800304 return 3;
305}
Yifan Hong50c9e252019-10-03 16:26:13 -0700306
307} // namespace health
308} // namespace hardware
309} // namespace android