healthd.cpp revision 9face5cad5b4ffb883b23e1c45aafac73c712fd6
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 "healthd"
18#define KLOG_LEVEL 6
19
20#include "healthd.h"
21#include "BatteryMonitor.h"
22
23#include <errno.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <batteryservice/BatteryService.h>
28#include <binder/IPCThreadState.h>
29#include <binder/ProcessState.h>
30#include <cutils/klog.h>
31#include <cutils/uevent.h>
32#include <sys/epoll.h>
33#include <sys/timerfd.h>
34
35using namespace android;
36
37// Periodic chores intervals in seconds
38#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
39#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
40
41static struct healthd_config healthd_config = {
42    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
43    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
44};
45
46#define POWER_SUPPLY_SUBSYSTEM "power_supply"
47
48// epoll events: uevent, wakealarm, binder
49#define MAX_EPOLL_EVENTS 3
50static int uevent_fd;
51static int wakealarm_fd;
52static int binder_fd;
53
54// -1 for no epoll timeout
55static int awake_poll_interval = -1;
56
57static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
58
59static BatteryMonitor* gBatteryMonitor;
60
61static bool nosvcmgr;
62
63static void wakealarm_set_interval(int interval) {
64    struct itimerspec itval;
65
66    if (wakealarm_fd == -1)
67            return;
68
69    wakealarm_wake_interval = interval;
70
71    if (interval == -1)
72        interval = 0;
73
74    itval.it_interval.tv_sec = interval;
75    itval.it_interval.tv_nsec = 0;
76    itval.it_value.tv_sec = interval;
77    itval.it_value.tv_nsec = 0;
78
79    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
80        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
81}
82
83static void battery_update(void) {
84    // Fast wake interval when on charger (watch for overheat);
85    // slow wake interval when on battery (watch for drained battery).
86
87   int new_wake_interval = gBatteryMonitor->update() ?
88       healthd_config.periodic_chores_interval_fast :
89           healthd_config.periodic_chores_interval_slow;
90
91    if (new_wake_interval != wakealarm_wake_interval)
92            wakealarm_set_interval(new_wake_interval);
93
94    // During awake periods poll at fast rate.  If wake alarm is set at fast
95    // rate then just use the alarm; if wake alarm is set at slow rate then
96    // poll at fast rate while awake and let alarm wake up at slow rate when
97    // asleep.
98
99    if (healthd_config.periodic_chores_interval_fast == -1)
100        awake_poll_interval = -1;
101    else
102        awake_poll_interval =
103            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
104                -1 : healthd_config.periodic_chores_interval_fast * 1000;
105}
106
107static void periodic_chores() {
108    battery_update();
109}
110
111static void uevent_init(void) {
112    uevent_fd = uevent_open_socket(64*1024, true);
113
114    if (uevent_fd >= 0)
115        fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
116    else
117        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
118}
119
120#define UEVENT_MSG_LEN 1024
121static void uevent_event(void) {
122    char msg[UEVENT_MSG_LEN+2];
123    char *cp;
124    int n;
125
126    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
127    if (n <= 0)
128        return;
129    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
130        return;
131
132    msg[n] = '\0';
133    msg[n+1] = '\0';
134    cp = msg;
135
136    while (*cp) {
137        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
138            battery_update();
139            break;
140        }
141
142        /* advance to after the next \0 */
143        while (*cp++)
144            ;
145    }
146}
147
148static void wakealarm_init(void) {
149    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
150    if (wakealarm_fd == -1) {
151        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
152        return;
153    }
154
155    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
156}
157
158static void wakealarm_event(void) {
159    unsigned long long wakeups;
160
161    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
162        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n");
163        return;
164    }
165
166    periodic_chores();
167}
168
169static void binder_init(void) {
170    ProcessState::self()->setThreadPoolMaxThreadCount(0);
171    IPCThreadState::self()->disableBackgroundScheduling(true);
172    IPCThreadState::self()->setupPolling(&binder_fd);
173}
174
175static void binder_event(void) {
176    IPCThreadState::self()->handlePolledCommands();
177}
178
179static void healthd_mainloop(void) {
180    struct epoll_event ev;
181    int epollfd;
182    int maxevents = 0;
183
184    epollfd = epoll_create(MAX_EPOLL_EVENTS);
185    if (epollfd == -1) {
186        KLOG_ERROR(LOG_TAG,
187                   "healthd_mainloop: epoll_create failed; errno=%d\n",
188                   errno);
189        return;
190    }
191
192    if (uevent_fd >= 0) {
193        ev.events = EPOLLIN | EPOLLWAKEUP;
194        ev.data.ptr = (void *)uevent_event;
195        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1)
196            KLOG_ERROR(LOG_TAG,
197                       "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n",
198                       errno);
199        else
200            maxevents++;
201    }
202
203    if (wakealarm_fd >= 0) {
204        ev.events = EPOLLIN | EPOLLWAKEUP;
205        ev.data.ptr = (void *)wakealarm_event;
206        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1)
207            KLOG_ERROR(LOG_TAG,
208                       "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n",
209                       errno);
210        else
211            maxevents++;
212   }
213
214    if (binder_fd >= 0) {
215        ev.events = EPOLLIN | EPOLLWAKEUP;
216        ev.data.ptr= (void *)binder_event;
217        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1)
218            KLOG_ERROR(LOG_TAG,
219                       "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n",
220                       errno);
221        else
222            maxevents++;
223   }
224
225    while (1) {
226        struct epoll_event events[maxevents];
227        int nevents;
228
229        IPCThreadState::self()->flushCommands();
230        nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval);
231
232        if (nevents == -1) {
233            if (errno == EINTR)
234                continue;
235            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
236            break;
237        }
238
239        for (int n = 0; n < nevents; ++n) {
240            if (events[n].data.ptr)
241                (*(void (*)())events[n].data.ptr)();
242        }
243    }
244
245    return;
246}
247
248int main(int argc, char **argv) {
249    int ch;
250
251    klog_set_level(KLOG_LEVEL);
252
253    while ((ch = getopt(argc, argv, "n")) != -1) {
254        switch (ch) {
255        case 'n':
256            nosvcmgr = true;
257            break;
258        case '?':
259        default:
260            KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
261        }
262    }
263
264    healthd_board_init(&healthd_config);
265    wakealarm_init();
266    uevent_init();
267    binder_init();
268    gBatteryMonitor = new BatteryMonitor();
269    gBatteryMonitor->init(nosvcmgr);
270
271    healthd_mainloop();
272    return 0;
273}
274