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-common"
18#define KLOG_LEVEL 6
19
20#include <healthd/healthd.h>
21#include <healthd/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#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
39  // Periodic chores fast interval in seconds
40  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
41#else
42  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
43#endif
44
45#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
46  // Periodic chores fast interval in seconds
47  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
48#else
49  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
50#endif
51
52static struct healthd_config healthd_config = {
53    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
54    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
55    .batteryStatusPath = String8(String8::kEmptyString),
56    .batteryHealthPath = String8(String8::kEmptyString),
57    .batteryPresentPath = String8(String8::kEmptyString),
58    .batteryCapacityPath = String8(String8::kEmptyString),
59    .batteryVoltagePath = String8(String8::kEmptyString),
60    .batteryTemperaturePath = String8(String8::kEmptyString),
61    .batteryTechnologyPath = String8(String8::kEmptyString),
62    .batteryCurrentNowPath = String8(String8::kEmptyString),
63    .batteryCurrentAvgPath = String8(String8::kEmptyString),
64    .batteryChargeCounterPath = String8(String8::kEmptyString),
65    .batteryFullChargePath = String8(String8::kEmptyString),
66    .batteryCycleCountPath = String8(String8::kEmptyString),
67    .energyCounter = NULL,
68    .boot_min_cap = 0,
69    .screen_on = NULL,
70};
71
72static int eventct;
73static int epollfd;
74
75#define POWER_SUPPLY_SUBSYSTEM "power_supply"
76
77// epoll_create() parameter is actually unused
78#define MAX_EPOLL_EVENTS 40
79static int uevent_fd;
80static int wakealarm_fd;
81
82// -1 for no epoll timeout
83static int awake_poll_interval = -1;
84
85static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
86
87static BatteryMonitor* gBatteryMonitor;
88
89struct healthd_mode_ops *healthd_mode_ops;
90
91int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
92    struct epoll_event ev;
93
94    ev.events = EPOLLIN;
95
96    if (wakeup == EVENT_WAKEUP_FD)
97        ev.events |= EPOLLWAKEUP;
98
99    ev.data.ptr = (void *)handler;
100    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
101        KLOG_ERROR(LOG_TAG,
102                   "epoll_ctl failed; errno=%d\n", errno);
103        return -1;
104    }
105
106    eventct++;
107    return 0;
108}
109
110static void wakealarm_set_interval(int interval) {
111    struct itimerspec itval;
112
113    if (wakealarm_fd == -1)
114            return;
115
116    wakealarm_wake_interval = interval;
117
118    if (interval == -1)
119        interval = 0;
120
121    itval.it_interval.tv_sec = interval;
122    itval.it_interval.tv_nsec = 0;
123    itval.it_value.tv_sec = interval;
124    itval.it_value.tv_nsec = 0;
125
126    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
127        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
128}
129
130status_t healthd_get_property(int id, struct BatteryProperty *val) {
131    return gBatteryMonitor->getProperty(id, val);
132}
133
134void healthd_battery_update(void) {
135    // Fast wake interval when on charger (watch for overheat);
136    // slow wake interval when on battery (watch for drained battery).
137
138   int new_wake_interval = gBatteryMonitor->update() ?
139       healthd_config.periodic_chores_interval_fast :
140           healthd_config.periodic_chores_interval_slow;
141
142    if (new_wake_interval != wakealarm_wake_interval)
143            wakealarm_set_interval(new_wake_interval);
144
145    // During awake periods poll at fast rate.  If wake alarm is set at fast
146    // rate then just use the alarm; if wake alarm is set at slow rate then
147    // poll at fast rate while awake and let alarm wake up at slow rate when
148    // asleep.
149
150    if (healthd_config.periodic_chores_interval_fast == -1)
151        awake_poll_interval = -1;
152    else
153        awake_poll_interval =
154            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
155                -1 : healthd_config.periodic_chores_interval_fast * 1000;
156}
157
158void healthd_dump_battery_state(int fd) {
159    gBatteryMonitor->dumpState(fd);
160    fsync(fd);
161}
162
163static void periodic_chores() {
164    healthd_battery_update();
165}
166
167#define UEVENT_MSG_LEN 2048
168static void uevent_event(uint32_t /*epevents*/) {
169    char msg[UEVENT_MSG_LEN+2];
170    char *cp;
171    int n;
172
173    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
174    if (n <= 0)
175        return;
176    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
177        return;
178
179    msg[n] = '\0';
180    msg[n+1] = '\0';
181    cp = msg;
182
183    while (*cp) {
184        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
185            healthd_battery_update();
186            break;
187        }
188
189        /* advance to after the next \0 */
190        while (*cp++)
191            ;
192    }
193}
194
195static void uevent_init(void) {
196    uevent_fd = uevent_open_socket(64*1024, true);
197
198    if (uevent_fd < 0) {
199        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
200        return;
201    }
202
203    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
204    if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
205        KLOG_ERROR(LOG_TAG,
206                   "register for uevent events failed\n");
207}
208
209static void wakealarm_event(uint32_t /*epevents*/) {
210    unsigned long long wakeups;
211
212    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
213        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
214        return;
215    }
216
217    periodic_chores();
218}
219
220static void wakealarm_init(void) {
221    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
222    if (wakealarm_fd == -1) {
223        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
224        return;
225    }
226
227    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
228        KLOG_ERROR(LOG_TAG,
229                   "Registration of wakealarm event failed\n");
230
231    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
232}
233
234static void healthd_mainloop(void) {
235    int nevents = 0;
236    while (1) {
237        struct epoll_event events[eventct];
238        int timeout = awake_poll_interval;
239        int mode_timeout;
240
241        /* Don't wait for first timer timeout to run periodic chores */
242        if (!nevents)
243            periodic_chores();
244
245        healthd_mode_ops->heartbeat();
246
247        mode_timeout = healthd_mode_ops->preparetowait();
248        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
249            timeout = mode_timeout;
250        nevents = epoll_wait(epollfd, events, eventct, timeout);
251        if (nevents == -1) {
252            if (errno == EINTR)
253                continue;
254            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
255            break;
256        }
257
258        for (int n = 0; n < nevents; ++n) {
259            if (events[n].data.ptr)
260                (*(void (*)(int))events[n].data.ptr)(events[n].events);
261        }
262    }
263
264    return;
265}
266
267static int healthd_init() {
268    epollfd = epoll_create(MAX_EPOLL_EVENTS);
269    if (epollfd == -1) {
270        KLOG_ERROR(LOG_TAG,
271                   "epoll_create failed; errno=%d\n",
272                   errno);
273        return -1;
274    }
275
276    healthd_board_init(&healthd_config);
277    healthd_mode_ops->init(&healthd_config);
278    wakealarm_init();
279    uevent_init();
280    gBatteryMonitor = new BatteryMonitor();
281    gBatteryMonitor->init(&healthd_config);
282    return 0;
283}
284
285int healthd_main() {
286    int ret;
287
288    klog_set_level(KLOG_LEVEL);
289
290    if (!healthd_mode_ops) {
291        KLOG_ERROR("healthd ops not set, exiting\n");
292        exit(1);
293    }
294
295    ret = healthd_init();
296    if (ret) {
297        KLOG_ERROR("Initialization failed, exiting\n");
298        exit(2);
299    }
300
301    healthd_mainloop();
302    KLOG_ERROR("Main loop terminated, exiting\n");
303    return 3;
304}
305