• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace android {
44 namespace hardware {
45 namespace health {
46 
HealthLoop()47 HealthLoop::HealthLoop() {
48     InitHealthdConfig(&healthd_config_);
49     awake_poll_interval_ = -1;
50     wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
51 }
52 
~HealthLoop()53 HealthLoop::~HealthLoop() {
54     LOG(FATAL) << "HealthLoop cannot be destroyed";
55 }
56 
RegisterEvent(int fd,BoundFunction func,EventWakeup wakeup)57 int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
58     CHECK(!reject_event_register_);
59 
60     auto* event_handler =
61             event_handlers_
62                     .emplace_back(std::make_unique<EventHandler>(EventHandler{this, fd, func}))
63                     .get();
64 
65     struct epoll_event ev;
66 
67     ev.events = EPOLLIN;
68 
69     if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
70 
71     ev.data.ptr = reinterpret_cast<void*>(event_handler);
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 // TODO(b/140330870): Use BPF instead.
126 #define UEVENT_MSG_LEN 2048
UeventEvent(uint32_t)127 void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
128     // No need to lock because uevent_fd_ is guaranteed to be initialized.
129 
130     char msg[UEVENT_MSG_LEN + 2];
131     char* cp;
132     int n;
133 
134     n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
135     if (n <= 0) return;
136     if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
137         return;
138 
139     msg[n] = '\0';
140     msg[n + 1] = '\0';
141     cp = msg;
142 
143     while (*cp) {
144         if (!strcmp(cp, "SUBSYSTEM=power_supply")) {
145             ScheduleBatteryUpdate();
146             break;
147         }
148 
149         /* advance to after the next \0 */
150         while (*cp++)
151             ;
152     }
153 }
154 
UeventInit(void)155 void HealthLoop::UeventInit(void) {
156     uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
157 
158     if (uevent_fd_ < 0) {
159         KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
160         return;
161     }
162 
163     fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
164     if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
165         KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
166 }
167 
WakeAlarmEvent(uint32_t)168 void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
169     // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
170 
171     unsigned long long wakeups;
172 
173     if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
174         KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
175         return;
176     }
177 
178     PeriodicChores();
179 }
180 
WakeAlarmInit(void)181 void HealthLoop::WakeAlarmInit(void) {
182     wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
183     if (wakealarm_fd_ == -1) {
184         KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
185         return;
186     }
187 
188     if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
189         KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
190 
191     WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
192 }
193 
MainLoop(void)194 void HealthLoop::MainLoop(void) {
195     int nevents = 0;
196     while (1) {
197         reject_event_register_ = true;
198         size_t eventct = event_handlers_.size();
199         struct epoll_event events[eventct];
200         int timeout = awake_poll_interval_;
201 
202         int mode_timeout;
203 
204         /* Don't wait for first timer timeout to run periodic chores */
205         if (!nevents) PeriodicChores();
206 
207         Heartbeat();
208 
209         mode_timeout = PrepareToWait();
210         if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
211         nevents = epoll_wait(epollfd_, events, eventct, timeout);
212         if (nevents == -1) {
213             if (errno == EINTR) continue;
214             KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
215             break;
216         }
217 
218         for (int n = 0; n < nevents; ++n) {
219             if (events[n].data.ptr) {
220                 auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
221                 event_handler->func(event_handler->object, events[n].events);
222             }
223         }
224     }
225 
226     return;
227 }
228 
InitInternal()229 int HealthLoop::InitInternal() {
230     epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
231     if (epollfd_ == -1) {
232         KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
233         return -1;
234     }
235 
236     // Call subclass's init for any additional init steps.
237     // Note that healthd_config_ is initialized before wakealarm_fd_; see
238     // AdjustUeventWakealarmPeriods().
239     Init(&healthd_config_);
240 
241     WakeAlarmInit();
242     UeventInit();
243 
244     return 0;
245 }
246 
StartLoop()247 int HealthLoop::StartLoop() {
248     int ret;
249 
250     klog_set_level(KLOG_LEVEL);
251 
252     ret = InitInternal();
253     if (ret) {
254         KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
255         return 2;
256     }
257 
258     MainLoop();
259     KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
260     return 3;
261 }
262 
263 }  // namespace health
264 }  // namespace hardware
265 }  // namespace android
266