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