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