healthd.cpp revision 020369d8724eff2b87350e54e157a609846166e4
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 <libgen.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <batteryservice/BatteryService.h>
30#include <cutils/klog.h>
31#include <cutils/uevent.h>
32#include <sys/epoll.h>
33#include <sys/timerfd.h>
34#include <utils/Errors.h>
35
36using namespace android;
37
38// Periodic chores intervals in seconds
39#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
40#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
41
42static struct healthd_config healthd_config = {
43    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
44    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
45    .batteryStatusPath = String8(String8::kEmptyString),
46    .batteryHealthPath = String8(String8::kEmptyString),
47    .batteryPresentPath = String8(String8::kEmptyString),
48    .batteryCapacityPath = String8(String8::kEmptyString),
49    .batteryVoltagePath = String8(String8::kEmptyString),
50    .batteryTemperaturePath = String8(String8::kEmptyString),
51    .batteryTechnologyPath = String8(String8::kEmptyString),
52    .batteryCurrentNowPath = String8(String8::kEmptyString),
53    .batteryCurrentAvgPath = String8(String8::kEmptyString),
54    .batteryChargeCounterPath = String8(String8::kEmptyString),
55};
56
57static int eventct;
58static int epollfd;
59
60#define POWER_SUPPLY_SUBSYSTEM "power_supply"
61
62// epoll_create() parameter is actually unused
63#define MAX_EPOLL_EVENTS 40
64static int uevent_fd;
65static int wakealarm_fd;
66static int binder_fd;
67
68// -1 for no epoll timeout
69static int awake_poll_interval = -1;
70
71static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
72
73static BatteryMonitor* gBatteryMonitor;
74
75struct healthd_mode_ops *healthd_mode_ops;
76
77// Android mode
78
79extern void healthd_mode_android_init(struct healthd_config *config);
80extern int healthd_mode_android_preparetowait(void);
81extern void healthd_mode_android_battery_update(
82    struct android::BatteryProperties *props);
83
84// Charger mode
85
86extern void healthd_mode_charger_init(struct healthd_config *config);
87extern int healthd_mode_charger_preparetowait(void);
88extern void healthd_mode_charger_heartbeat(void);
89extern void healthd_mode_charger_battery_update(
90    struct android::BatteryProperties *props);
91
92// NOPs for modes that need no special action
93
94static void healthd_mode_nop_init(struct healthd_config *config);
95static int healthd_mode_nop_preparetowait(void);
96static void healthd_mode_nop_heartbeat(void);
97static void healthd_mode_nop_battery_update(
98    struct android::BatteryProperties *props);
99
100static struct healthd_mode_ops android_ops = {
101    .init = healthd_mode_android_init,
102    .preparetowait = healthd_mode_android_preparetowait,
103    .heartbeat = healthd_mode_nop_heartbeat,
104    .battery_update = healthd_mode_android_battery_update,
105};
106
107static struct healthd_mode_ops charger_ops = {
108    .init = healthd_mode_charger_init,
109    .preparetowait = healthd_mode_charger_preparetowait,
110    .heartbeat = healthd_mode_charger_heartbeat,
111    .battery_update = healthd_mode_charger_battery_update,
112};
113
114static struct healthd_mode_ops recovery_ops = {
115    .init = healthd_mode_nop_init,
116    .preparetowait = healthd_mode_nop_preparetowait,
117    .heartbeat = healthd_mode_nop_heartbeat,
118    .battery_update = healthd_mode_nop_battery_update,
119};
120
121static void healthd_mode_nop_init(struct healthd_config *config) {
122}
123
124static int healthd_mode_nop_preparetowait(void) {
125    return -1;
126}
127
128static void healthd_mode_nop_heartbeat(void) {
129}
130
131static void healthd_mode_nop_battery_update(
132    struct android::BatteryProperties *props) {
133}
134
135int healthd_register_event(int fd, void (*handler)(uint32_t)) {
136    struct epoll_event ev;
137
138    ev.events = EPOLLIN | EPOLLWAKEUP;
139    ev.data.ptr = (void *)handler;
140    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
141        KLOG_ERROR(LOG_TAG,
142                   "epoll_ctl failed; errno=%d\n", errno);
143        return -1;
144    }
145
146    eventct++;
147    return 0;
148}
149
150static void wakealarm_set_interval(int interval) {
151    struct itimerspec itval;
152
153    if (wakealarm_fd == -1)
154            return;
155
156    wakealarm_wake_interval = interval;
157
158    if (interval == -1)
159        interval = 0;
160
161    itval.it_interval.tv_sec = interval;
162    itval.it_interval.tv_nsec = 0;
163    itval.it_value.tv_sec = interval;
164    itval.it_value.tv_nsec = 0;
165
166    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
167        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
168}
169
170status_t healthd_get_property(int id, struct BatteryProperty *val) {
171    return gBatteryMonitor->getProperty(id, val);
172}
173
174void healthd_battery_update(void) {
175    // Fast wake interval when on charger (watch for overheat);
176    // slow wake interval when on battery (watch for drained battery).
177
178   int new_wake_interval = gBatteryMonitor->update() ?
179       healthd_config.periodic_chores_interval_fast :
180           healthd_config.periodic_chores_interval_slow;
181
182    if (new_wake_interval != wakealarm_wake_interval)
183            wakealarm_set_interval(new_wake_interval);
184
185    // During awake periods poll at fast rate.  If wake alarm is set at fast
186    // rate then just use the alarm; if wake alarm is set at slow rate then
187    // poll at fast rate while awake and let alarm wake up at slow rate when
188    // asleep.
189
190    if (healthd_config.periodic_chores_interval_fast == -1)
191        awake_poll_interval = -1;
192    else
193        awake_poll_interval =
194            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
195                -1 : healthd_config.periodic_chores_interval_fast * 1000;
196}
197
198void healthd_dump_battery_state(int fd) {
199    gBatteryMonitor->dumpState(fd);
200    fsync(fd);
201}
202
203static void periodic_chores() {
204    healthd_battery_update();
205}
206
207#define UEVENT_MSG_LEN 1024
208static void uevent_event(uint32_t epevents) {
209    char msg[UEVENT_MSG_LEN+2];
210    char *cp;
211    int n;
212
213    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
214    if (n <= 0)
215        return;
216    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
217        return;
218
219    msg[n] = '\0';
220    msg[n+1] = '\0';
221    cp = msg;
222
223    while (*cp) {
224        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
225            healthd_battery_update();
226            break;
227        }
228
229        /* advance to after the next \0 */
230        while (*cp++)
231            ;
232    }
233}
234
235static void uevent_init(void) {
236    uevent_fd = uevent_open_socket(64*1024, true);
237
238    if (uevent_fd < 0) {
239        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
240        return;
241    }
242
243    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
244    if (healthd_register_event(uevent_fd, uevent_event))
245        KLOG_ERROR(LOG_TAG,
246                   "register for uevent events failed\n");
247}
248
249static void wakealarm_event(uint32_t epevents) {
250    unsigned long long wakeups;
251
252    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
253        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
254        return;
255    }
256
257    periodic_chores();
258}
259
260static void wakealarm_init(void) {
261    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
262    if (wakealarm_fd == -1) {
263        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
264        return;
265    }
266
267    if (healthd_register_event(wakealarm_fd, wakealarm_event))
268        KLOG_ERROR(LOG_TAG,
269                   "Registration of wakealarm event failed\n");
270
271    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
272}
273
274static void healthd_mainloop(void) {
275    while (1) {
276        struct epoll_event events[eventct];
277        int nevents;
278        int timeout = awake_poll_interval;
279        int mode_timeout;
280
281        mode_timeout = healthd_mode_ops->preparetowait();
282        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
283            timeout = mode_timeout;
284        nevents = epoll_wait(epollfd, events, eventct, timeout);
285
286        if (nevents == -1) {
287            if (errno == EINTR)
288                continue;
289            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
290            break;
291        }
292
293        for (int n = 0; n < nevents; ++n) {
294            if (events[n].data.ptr)
295                (*(void (*)(int))events[n].data.ptr)(events[n].events);
296        }
297
298        if (!nevents)
299            periodic_chores();
300
301        healthd_mode_ops->heartbeat();
302    }
303
304    return;
305}
306
307static int healthd_init() {
308    epollfd = epoll_create(MAX_EPOLL_EVENTS);
309    if (epollfd == -1) {
310        KLOG_ERROR(LOG_TAG,
311                   "epoll_create failed; errno=%d\n",
312                   errno);
313        return -1;
314    }
315
316    healthd_mode_ops->init(&healthd_config);
317    healthd_board_init(&healthd_config);
318    wakealarm_init();
319    uevent_init();
320    gBatteryMonitor = new BatteryMonitor();
321    gBatteryMonitor->init(&healthd_config);
322    return 0;
323}
324
325int main(int argc, char **argv) {
326    int ch;
327    int ret;
328
329    klog_set_level(KLOG_LEVEL);
330    healthd_mode_ops = &android_ops;
331
332    if (!strcmp(basename(argv[0]), "charger")) {
333        healthd_mode_ops = &charger_ops;
334    } else {
335        while ((ch = getopt(argc, argv, "cr")) != -1) {
336            switch (ch) {
337            case 'c':
338                healthd_mode_ops = &charger_ops;
339                break;
340            case 'r':
341                healthd_mode_ops = &recovery_ops;
342                break;
343            case '?':
344            default:
345                KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
346                exit(1);
347            }
348        }
349    }
350
351    ret = healthd_init();
352    if (ret) {
353        KLOG_ERROR("Initialization failed, exiting\n");
354        exit(2);
355    }
356
357    healthd_mainloop();
358    KLOG_ERROR("Main loop terminated, exiting\n");
359    return 3;
360}
361