1 /*
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
17 #define LOG_TAG "HealthLoop"
18 #define KLOG_LEVEL 6
19
20 #include <health/HealthLoop.h>
21
22 #include <errno.h>
23 #include <sys/epoll.h> // epoll_create1(), epoll_ctl(), epoll_wait()
24 #include <sys/timerfd.h>
25 #include <unistd.h> // read()
26
27 #include <android-base/logging.h>
28 #include <batteryservice/BatteryService.h>
29 #include <cutils/klog.h> // KLOG_*()
30 #include <cutils/uevent.h>
31 #include <healthd/healthd.h>
32
33 #include <BpfSyscallWrappers.h>
34 #include <health/utils.h>
35
36 using android::base::ErrnoError;
37 using android::base::Result;
38 using android::base::unique_fd;
39 using namespace android;
40 using namespace std::chrono_literals;
41
42 namespace android {
43 namespace hardware {
44 namespace health {
45
46 static constexpr uint32_t kUeventMsgLen = 2048;
47
HealthLoop()48 HealthLoop::HealthLoop() {
49 InitHealthdConfig(&healthd_config_);
50 awake_poll_interval_ = -1;
51 wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
52 }
53
~HealthLoop()54 HealthLoop::~HealthLoop() {
55 LOG(FATAL) << "HealthLoop cannot be destroyed";
56 }
57
RegisterEvent(int fd,BoundFunction func,EventWakeup wakeup)58 int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
59 CHECK(!reject_event_register_);
60
61 auto* event_handler = event_handlers_
62 .emplace_back(std::make_unique<EventHandler>(
63 EventHandler{this, fd, std::move(func)}))
64 .get();
65
66 struct epoll_event ev = {
67 .events = EPOLLIN | EPOLLERR,
68 .data.ptr = reinterpret_cast<void*>(event_handler),
69 };
70
71 if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
72
73 if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
74 KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
75 return -1;
76 }
77
78 return 0;
79 }
80
WakeAlarmSetInterval(int interval)81 void HealthLoop::WakeAlarmSetInterval(int interval) {
82 struct itimerspec itval;
83
84 if (wakealarm_fd_ == -1) return;
85
86 wakealarm_wake_interval_ = interval;
87
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
95 if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
96 KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
97 }
98
AdjustWakealarmPeriods(bool charger_online)99 void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
100 // Fast wake interval when on charger (watch for overheat);
101 // slow wake interval when on battery (watch for drained battery).
102
103 int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
104 : healthd_config_.periodic_chores_interval_slow;
105
106 if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
107
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
113 if (healthd_config_.periodic_chores_interval_fast == -1)
114 awake_poll_interval_ = -1;
115 else
116 awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
117 ? -1
118 : healthd_config_.periodic_chores_interval_fast * 1000;
119 }
120
PeriodicChores()121 void HealthLoop::PeriodicChores() {
122 ScheduleBatteryUpdate();
123 }
124
125 // Returns true if and only if the battery statistics should be updated.
RecvUevents()126 bool 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 && errno == ENOBUFS) {
132 update_stats = true;
133 }
134 if (n <= 0) return update_stats;
135 if (n >= kUeventMsgLen) {
136 // too long -- discard
137 continue;
138 }
139 if (update_stats) {
140 continue;
141 }
142
143 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
UeventEvent(uint32_t)158 void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
159 if (RecvUevents()) {
160 ScheduleBatteryUpdate();
161 }
162 }
163
164 // 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.
AttachFilter(int uevent_fd)167 Result<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
UeventInit(void)182 void HealthLoop::UeventInit(void) {
183 uevent_fd_.reset(uevent_create_socket(kUeventMsgLen, true));
184
185 if (uevent_fd_ < 0) {
186 KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
187 return;
188 }
189
190 fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
191
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.";
197 KLOG_WARNING(LOG_TAG, "%s\n", error_msg.c_str());
198 } else {
199 KLOG_INFO(LOG_TAG, "Successfully attached the BPF filter to the uevent socket\n");
200 }
201
202 if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
203 KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
204
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 }
210 }
211
WakeAlarmEvent(uint32_t)212 void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
213 // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
214
215 unsigned long long wakeups;
216
217 if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
218 KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
219 return;
220 }
221
222 PeriodicChores();
223 }
224
WakeAlarmInit(void)225 void HealthLoop::WakeAlarmInit(void) {
226 wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
227 if (wakealarm_fd_ == -1) {
228 KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
229 return;
230 }
231
232 if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
233 KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
234
235 WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
236 }
237
MainLoop(void)238 void HealthLoop::MainLoop(void) {
239 int nevents = 0;
240 while (1) {
241 reject_event_register_ = true;
242 size_t eventct = event_handlers_.size();
243 struct epoll_event events[eventct];
244 int timeout = awake_poll_interval_;
245
246 int mode_timeout;
247
248 /* Don't wait for first timer timeout to run periodic chores */
249 if (!nevents) PeriodicChores();
250
251 Heartbeat();
252
253 mode_timeout = PrepareToWait();
254 if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
255 nevents = epoll_wait(epollfd_, events, eventct, timeout);
256 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) {
263 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 }
267 }
268 }
269
270 return;
271 }
272
InitInternal()273 int HealthLoop::InitInternal() {
274 epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
275 if (epollfd_ == -1) {
276 KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
277 return -1;
278 }
279
280 // 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();
287
288 return 0;
289 }
290
StartLoop()291 int HealthLoop::StartLoop() {
292 int ret;
293
294 klog_set_level(KLOG_LEVEL);
295
296 ret = InitInternal();
297 if (ret) {
298 KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
299 return 2;
300 }
301
302 MainLoop();
303 KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
304 return 3;
305 }
306
307 } // namespace health
308 } // namespace hardware
309 } // namespace android
310