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 <libgen.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/epoll.h>
28 #include <sys/timerfd.h>
29 #include <unistd.h>
30 
31 #include <android-base/logging.h>
32 #include <batteryservice/BatteryService.h>
33 #include <cutils/klog.h>
34 #include <cutils/uevent.h>
35 #include <healthd/healthd.h>
36 #include <utils/Errors.h>
37 
38 #include <health/utils.h>
39 
40 using namespace android;
41 using namespace std::chrono_literals;
42 
43 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
44 
45 namespace android {
46 namespace hardware {
47 namespace health {
48 
HealthLoop()49 HealthLoop::HealthLoop() {
50     InitHealthdConfig(&healthd_config_);
51     awake_poll_interval_ = -1;
52     wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
53 }
54 
~HealthLoop()55 HealthLoop::~HealthLoop() {
56     LOG(FATAL) << "HealthLoop cannot be destroyed";
57 }
58 
RegisterEvent(int fd,BoundFunction func,EventWakeup wakeup)59 int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
60     CHECK(!reject_event_register_);
61 
62     auto* event_handler =
63             event_handlers_
64                     .emplace_back(std::make_unique<EventHandler>(EventHandler{this, fd, func}))
65                     .get();
66 
67     struct epoll_event ev;
68 
69     ev.events = EPOLLIN;
70 
71     if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
72 
73     ev.data.ptr = reinterpret_cast<void*>(event_handler);
74 
75     if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
76         KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
77         return -1;
78     }
79 
80     return 0;
81 }
82 
WakeAlarmSetInterval(int interval)83 void HealthLoop::WakeAlarmSetInterval(int interval) {
84     struct itimerspec itval;
85 
86     if (wakealarm_fd_ == -1) return;
87 
88     wakealarm_wake_interval_ = interval;
89 
90     if (interval == -1) interval = 0;
91 
92     itval.it_interval.tv_sec = interval;
93     itval.it_interval.tv_nsec = 0;
94     itval.it_value.tv_sec = interval;
95     itval.it_value.tv_nsec = 0;
96 
97     if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
98         KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
99 }
100 
AdjustWakealarmPeriods(bool charger_online)101 void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
102     // Fast wake interval when on charger (watch for overheat);
103     // slow wake interval when on battery (watch for drained battery).
104 
105     int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
106                                            : healthd_config_.periodic_chores_interval_slow;
107 
108     if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
109 
110     // During awake periods poll at fast rate.  If wake alarm is set at fast
111     // rate then just use the alarm; if wake alarm is set at slow rate then
112     // poll at fast rate while awake and let alarm wake up at slow rate when
113     // asleep.
114 
115     if (healthd_config_.periodic_chores_interval_fast == -1)
116         awake_poll_interval_ = -1;
117     else
118         awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
119                                        ? -1
120                                        : healthd_config_.periodic_chores_interval_fast * 1000;
121 }
122 
PeriodicChores()123 void HealthLoop::PeriodicChores() {
124     ScheduleBatteryUpdate();
125 }
126 
127 // TODO(b/140330870): Use BPF instead.
128 #define UEVENT_MSG_LEN 2048
UeventEvent(uint32_t)129 void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
130     // No need to lock because uevent_fd_ is guaranteed to be initialized.
131 
132     char msg[UEVENT_MSG_LEN + 2];
133     char* cp;
134     int n;
135 
136     n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
137     if (n <= 0) return;
138     if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
139         return;
140 
141     msg[n] = '\0';
142     msg[n + 1] = '\0';
143     cp = msg;
144 
145     while (*cp) {
146         if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
147             ScheduleBatteryUpdate();
148             break;
149         }
150 
151         /* advance to after the next \0 */
152         while (*cp++)
153             ;
154     }
155 }
156 
UeventInit(void)157 void HealthLoop::UeventInit(void) {
158     uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
159 
160     if (uevent_fd_ < 0) {
161         KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
162         return;
163     }
164 
165     fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
166     if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
167         KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
168 }
169 
WakeAlarmEvent(uint32_t)170 void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
171     // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
172 
173     unsigned long long wakeups;
174 
175     if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
176         KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
177         return;
178     }
179 
180     PeriodicChores();
181 }
182 
WakeAlarmInit(void)183 void HealthLoop::WakeAlarmInit(void) {
184     wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
185     if (wakealarm_fd_ == -1) {
186         KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
187         return;
188     }
189 
190     if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
191         KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
192 
193     WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
194 }
195 
MainLoop(void)196 void HealthLoop::MainLoop(void) {
197     int nevents = 0;
198     while (1) {
199         reject_event_register_ = true;
200         size_t eventct = event_handlers_.size();
201         struct epoll_event events[eventct];
202         int timeout = awake_poll_interval_;
203 
204         int mode_timeout;
205 
206         /* Don't wait for first timer timeout to run periodic chores */
207         if (!nevents) PeriodicChores();
208 
209         Heartbeat();
210 
211         mode_timeout = PrepareToWait();
212         if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
213         nevents = epoll_wait(epollfd_, events, eventct, timeout);
214         if (nevents == -1) {
215             if (errno == EINTR) continue;
216             KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
217             break;
218         }
219 
220         for (int n = 0; n < nevents; ++n) {
221             if (events[n].data.ptr) {
222                 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
223                 event_handler->func(event_handler->object, events[n].events);
224             }
225         }
226     }
227 
228     return;
229 }
230 
InitInternal()231 int HealthLoop::InitInternal() {
232     epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
233     if (epollfd_ == -1) {
234         KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
235         return -1;
236     }
237 
238     // Call subclass's init for any additional init steps.
239     // Note that healthd_config_ is initialized before wakealarm_fd_; see
240     // AdjustUeventWakealarmPeriods().
241     Init(&healthd_config_);
242 
243     WakeAlarmInit();
244     UeventInit();
245 
246     return 0;
247 }
248 
StartLoop()249 int HealthLoop::StartLoop() {
250     int ret;
251 
252     klog_set_level(KLOG_LEVEL);
253 
254     ret = InitInternal();
255     if (ret) {
256         KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
257         return 2;
258     }
259 
260     MainLoop();
261     KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
262     return 3;
263 }
264 
265 }  // namespace health
266 }  // namespace hardware
267 }  // namespace android
268