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