xref: /aosp_15_r20/hardware/interfaces/health/utils/libhealthloop/HealthLoop.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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