Merge changes from topic "libhealthloop" am: 9a41dd7f5a
am: d902e230d9
Change-Id: I9d812b05d0493cce059d0b434aaabba5466b0404
diff --git a/health/2.0/default/Android.bp b/health/2.0/default/Android.bp
index 1c455d3..3c5877e 100644
--- a/health/2.0/default/Android.bp
+++ b/health/2.0/default/Android.bp
@@ -31,7 +31,11 @@
vendor_available: true,
srcs: [
"Health.cpp",
- "healthd_common.cpp",
+ "healthd_common_adapter.cpp",
+ ],
+
+ whole_static_libs: [
+ "libhealthloop",
],
export_include_dirs: ["include"],
diff --git a/health/2.0/default/healthd_common.cpp b/health/2.0/default/healthd_common.cpp
deleted file mode 100644
index b5fdc8e..0000000
--- a/health/2.0/default/healthd_common.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.health@2.0-impl"
-#define KLOG_LEVEL 6
-
-#include <healthd/BatteryMonitor.h>
-#include <healthd/healthd.h>
-
-#include <batteryservice/BatteryService.h>
-#include <cutils/klog.h>
-#include <cutils/uevent.h>
-#include <errno.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <unistd.h>
-#include <utils/Errors.h>
-
-#include <health2/Health.h>
-
-using namespace android;
-
-// Periodic chores fast interval in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-// Periodic chores fast interval in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
-
-static struct healthd_config healthd_config = {
- .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
- .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
- .batteryStatusPath = String8(String8::kEmptyString),
- .batteryHealthPath = String8(String8::kEmptyString),
- .batteryPresentPath = String8(String8::kEmptyString),
- .batteryCapacityPath = String8(String8::kEmptyString),
- .batteryVoltagePath = String8(String8::kEmptyString),
- .batteryTemperaturePath = String8(String8::kEmptyString),
- .batteryTechnologyPath = String8(String8::kEmptyString),
- .batteryCurrentNowPath = String8(String8::kEmptyString),
- .batteryCurrentAvgPath = String8(String8::kEmptyString),
- .batteryChargeCounterPath = String8(String8::kEmptyString),
- .batteryFullChargePath = String8(String8::kEmptyString),
- .batteryCycleCountPath = String8(String8::kEmptyString),
- .energyCounter = NULL,
- .boot_min_cap = 0,
- .screen_on = NULL,
-};
-
-static int eventct;
-static int epollfd;
-
-#define POWER_SUPPLY_SUBSYSTEM "power_supply"
-
-static int uevent_fd;
-static int wakealarm_fd;
-
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
-
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-
-using ::android::hardware::health::V2_0::implementation::Health;
-
-struct healthd_mode_ops* healthd_mode_ops = nullptr;
-
-int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
- struct epoll_event ev;
-
- ev.events = EPOLLIN;
-
- if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
-
- ev.data.ptr = (void*)handler;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
- return -1;
- }
-
- eventct++;
- return 0;
-}
-
-static void wakealarm_set_interval(int interval) {
- struct itimerspec itval;
-
- if (wakealarm_fd == -1) return;
-
- wakealarm_wake_interval = interval;
-
- if (interval == -1) interval = 0;
-
- itval.it_interval.tv_sec = interval;
- itval.it_interval.tv_nsec = 0;
- itval.it_value.tv_sec = interval;
- itval.it_value.tv_nsec = 0;
-
- if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
- KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
-}
-
-void healthd_battery_update_internal(bool charger_online) {
- // Fast wake interval when on charger (watch for overheat);
- // slow wake interval when on battery (watch for drained battery).
-
- int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast
- : healthd_config.periodic_chores_interval_slow;
-
- if (new_wake_interval != wakealarm_wake_interval) wakealarm_set_interval(new_wake_interval);
-
- // During awake periods poll at fast rate. If wake alarm is set at fast
- // rate then just use the alarm; if wake alarm is set at slow rate then
- // poll at fast rate while awake and let alarm wake up at slow rate when
- // asleep.
-
- if (healthd_config.periodic_chores_interval_fast == -1)
- awake_poll_interval = -1;
- else
- awake_poll_interval = new_wake_interval == healthd_config.periodic_chores_interval_fast
- ? -1
- : healthd_config.periodic_chores_interval_fast * 1000;
-}
-
-static void healthd_battery_update(void) {
- Health::getImplementation()->update();
-}
-
-static void periodic_chores() {
- healthd_battery_update();
-}
-
-#define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
- char msg[UEVENT_MSG_LEN + 2];
- char* cp;
- int n;
-
- n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
- if (n <= 0) return;
- if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
- return;
-
- msg[n] = '\0';
- msg[n + 1] = '\0';
- cp = msg;
-
- while (*cp) {
- if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
- healthd_battery_update();
- break;
- }
-
- /* advance to after the next \0 */
- while (*cp++)
- ;
- }
-}
-
-static void uevent_init(void) {
- uevent_fd = uevent_open_socket(64 * 1024, true);
-
- if (uevent_fd < 0) {
- KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
- return;
- }
-
- fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
- if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
- KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
-}
-
-static void wakealarm_event(uint32_t /*epevents*/) {
- unsigned long long wakeups;
-
- if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
- KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
- return;
- }
-
- periodic_chores();
-}
-
-static void wakealarm_init(void) {
- wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
- if (wakealarm_fd == -1) {
- KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
- return;
- }
-
- if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
- KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
-
- wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
-}
-
-static void healthd_mainloop(void) {
- int nevents = 0;
- while (1) {
- struct epoll_event events[eventct];
- int timeout = awake_poll_interval;
- int mode_timeout;
-
- /* Don't wait for first timer timeout to run periodic chores */
- if (!nevents) periodic_chores();
-
- healthd_mode_ops->heartbeat();
-
- mode_timeout = healthd_mode_ops->preparetowait();
- if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
- nevents = epoll_wait(epollfd, events, eventct, timeout);
- if (nevents == -1) {
- if (errno == EINTR) continue;
- KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
- break;
- }
-
- for (int n = 0; n < nevents; ++n) {
- if (events[n].data.ptr) (*(void (*)(int))events[n].data.ptr)(events[n].events);
- }
- }
-
- return;
-}
-
-static int healthd_init() {
- epollfd = epoll_create1(EPOLL_CLOEXEC);
- if (epollfd == -1) {
- KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
- return -1;
- }
-
- healthd_mode_ops->init(&healthd_config);
- wakealarm_init();
- uevent_init();
-
- return 0;
-}
-
-int healthd_main() {
- int ret;
-
- klog_set_level(KLOG_LEVEL);
-
- if (!healthd_mode_ops) {
- KLOG_ERROR("healthd ops not set, exiting\n");
- exit(1);
- }
-
- ret = healthd_init();
- if (ret) {
- KLOG_ERROR("Initialization failed, exiting\n");
- exit(2);
- }
-
- healthd_mainloop();
- KLOG_ERROR("Main loop terminated, exiting\n");
- return 3;
-}
diff --git a/health/2.0/default/healthd_common_adapter.cpp b/health/2.0/default/healthd_common_adapter.cpp
new file mode 100644
index 0000000..8fc689d
--- /dev/null
+++ b/health/2.0/default/healthd_common_adapter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Support legacy functions in healthd/healthd.h using healthd_mode_ops.
+// New code should use HealthLoop directly instead.
+
+#include <memory>
+
+#include <cutils/klog.h>
+#include <health/HealthLoop.h>
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+
+using android::hardware::health::HealthLoop;
+using android::hardware::health::V2_0::implementation::Health;
+
+struct healthd_mode_ops* healthd_mode_ops = nullptr;
+
+// Adapter of HealthLoop to use legacy healthd_mode_ops.
+class HealthLoopAdapter : public HealthLoop {
+ public:
+ // Expose internal functions, assuming clients calls them in the same thread
+ // where StartLoop is called.
+ int RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
+ return HealthLoop::RegisterEvent(fd, func, wakeup);
+ }
+ void AdjustWakealarmPeriods(bool charger_online) {
+ return HealthLoop::AdjustWakealarmPeriods(charger_online);
+ }
+ protected:
+ void Init(healthd_config* config) override { healthd_mode_ops->init(config); }
+ void Heartbeat() override { healthd_mode_ops->heartbeat(); }
+ int PrepareToWait() override { return healthd_mode_ops->preparetowait(); }
+ void ScheduleBatteryUpdate() override { Health::getImplementation()->update(); }
+};
+static std::unique_ptr<HealthLoopAdapter> health_loop;
+
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
+ auto wrapped_handler = [handler](auto*, uint32_t epevents) { handler(epevents); };
+ return health_loop->RegisterEvent(fd, wrapped_handler, wakeup);
+}
+
+void healthd_battery_update_internal(bool charger_online) {
+ health_loop->AdjustWakealarmPeriods(charger_online);
+}
+
+int healthd_main() {
+ if (!healthd_mode_ops) {
+ KLOG_ERROR("healthd ops not set, exiting\n");
+ exit(1);
+ }
+
+ health_loop = std::make_unique<HealthLoopAdapter>();
+
+ int ret = health_loop->StartLoop();
+
+ // Should not reach here. The following will exit().
+ health_loop.reset();
+
+ return ret;
+}
diff --git a/health/utils/libhealthloop/Android.bp b/health/utils/libhealthloop/Android.bp
new file mode 100644
index 0000000..de0f24f
--- /dev/null
+++ b/health/utils/libhealthloop/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libhealthloop",
+ vendor_available: true,
+ recovery_available: true,
+ srcs: [
+ "HealthLoop.cpp",
+ "utils.cpp",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libbase",
+ ],
+ header_libs: [
+ "libbatteryservice_headers",
+ "libhealthd_headers",
+ "libutils_headers",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/health/utils/libhealthloop/HealthLoop.cpp b/health/utils/libhealthloop/HealthLoop.cpp
new file mode 100644
index 0000000..3f4b5bc
--- /dev/null
+++ b/health/utils/libhealthloop/HealthLoop.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HealthLoop"
+#define KLOG_LEVEL 6
+
+#include <health/HealthLoop.h>
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/uevent.h>
+#include <healthd/healthd.h>
+#include <utils/Errors.h>
+
+#include <health/utils.h>
+
+using namespace android;
+using namespace std::chrono_literals;
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+
+namespace android {
+namespace hardware {
+namespace health {
+
+HealthLoop::HealthLoop() {
+ InitHealthdConfig(&healthd_config_);
+ awake_poll_interval_ = -1;
+ wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
+}
+
+HealthLoop::~HealthLoop() {
+ LOG(FATAL) << "HealthLoop cannot be destroyed";
+}
+
+int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
+ CHECK(!reject_event_register_);
+
+ auto* event_handler =
+ event_handlers_
+ .emplace_back(std::make_unique<EventHandler>(EventHandler{this, fd, func}))
+ .get();
+
+ struct epoll_event ev;
+
+ ev.events = EPOLLIN;
+
+ if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;
+
+ ev.data.ptr = reinterpret_cast<void*>(event_handler);
+
+ if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+void HealthLoop::WakeAlarmSetInterval(int interval) {
+ struct itimerspec itval;
+
+ if (wakealarm_fd_ == -1) return;
+
+ wakealarm_wake_interval_ = interval;
+
+ if (interval == -1) interval = 0;
+
+ itval.it_interval.tv_sec = interval;
+ itval.it_interval.tv_nsec = 0;
+ itval.it_value.tv_sec = interval;
+ itval.it_value.tv_nsec = 0;
+
+ if (timerfd_settime(wakealarm_fd_, 0, &itval, NULL) == -1)
+ KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
+}
+
+void HealthLoop::AdjustWakealarmPeriods(bool charger_online) {
+ // Fast wake interval when on charger (watch for overheat);
+ // slow wake interval when on battery (watch for drained battery).
+
+ int new_wake_interval = charger_online ? healthd_config_.periodic_chores_interval_fast
+ : healthd_config_.periodic_chores_interval_slow;
+
+ if (new_wake_interval != wakealarm_wake_interval_) WakeAlarmSetInterval(new_wake_interval);
+
+ // During awake periods poll at fast rate. If wake alarm is set at fast
+ // rate then just use the alarm; if wake alarm is set at slow rate then
+ // poll at fast rate while awake and let alarm wake up at slow rate when
+ // asleep.
+
+ if (healthd_config_.periodic_chores_interval_fast == -1)
+ awake_poll_interval_ = -1;
+ else
+ awake_poll_interval_ = new_wake_interval == healthd_config_.periodic_chores_interval_fast
+ ? -1
+ : healthd_config_.periodic_chores_interval_fast * 1000;
+}
+
+void HealthLoop::PeriodicChores() {
+ ScheduleBatteryUpdate();
+}
+
+// TODO(b/140330870): Use BPF instead.
+#define UEVENT_MSG_LEN 2048
+void HealthLoop::UeventEvent(uint32_t /*epevents*/) {
+ // No need to lock because uevent_fd_ is guaranteed to be initialized.
+
+ char msg[UEVENT_MSG_LEN + 2];
+ char* cp;
+ int n;
+
+ n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
+ if (n <= 0) return;
+ if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
+ return;
+
+ msg[n] = '\0';
+ msg[n + 1] = '\0';
+ cp = msg;
+
+ while (*cp) {
+ if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
+ ScheduleBatteryUpdate();
+ break;
+ }
+
+ /* advance to after the next \0 */
+ while (*cp++)
+ ;
+ }
+}
+
+void HealthLoop::UeventInit(void) {
+ uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
+
+ if (uevent_fd_ < 0) {
+ KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+ return;
+ }
+
+ fcntl(uevent_fd_, F_SETFL, O_NONBLOCK);
+ if (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
+ KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
+}
+
+void HealthLoop::WakeAlarmEvent(uint32_t /*epevents*/) {
+ // No need to lock because wakealarm_fd_ is guaranteed to be initialized.
+
+ unsigned long long wakeups;
+
+ if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
+ KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
+ return;
+ }
+
+ PeriodicChores();
+}
+
+void HealthLoop::WakeAlarmInit(void) {
+ wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
+ if (wakealarm_fd_ == -1) {
+ KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
+ return;
+ }
+
+ if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
+ KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
+
+ WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
+}
+
+void HealthLoop::MainLoop(void) {
+ int nevents = 0;
+ while (1) {
+ reject_event_register_ = true;
+ size_t eventct = event_handlers_.size();
+ struct epoll_event events[eventct];
+ int timeout = awake_poll_interval_;
+
+ int mode_timeout;
+
+ /* Don't wait for first timer timeout to run periodic chores */
+ if (!nevents) PeriodicChores();
+
+ Heartbeat();
+
+ mode_timeout = PrepareToWait();
+ if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
+ nevents = epoll_wait(epollfd_, events, eventct, timeout);
+ if (nevents == -1) {
+ if (errno == EINTR) continue;
+ KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
+ break;
+ }
+
+ for (int n = 0; n < nevents; ++n) {
+ if (events[n].data.ptr) {
+ auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
+ event_handler->func(event_handler->object, events[n].events);
+ }
+ }
+ }
+
+ return;
+}
+
+int HealthLoop::InitInternal() {
+ epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
+ if (epollfd_ == -1) {
+ KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
+ return -1;
+ }
+
+ // Call subclass's init for any additional init steps.
+ // Note that healthd_config_ is initialized before wakealarm_fd_; see
+ // AdjustUeventWakealarmPeriods().
+ Init(&healthd_config_);
+
+ WakeAlarmInit();
+ UeventInit();
+
+ return 0;
+}
+
+int HealthLoop::StartLoop() {
+ int ret;
+
+ klog_set_level(KLOG_LEVEL);
+
+ ret = InitInternal();
+ if (ret) {
+ KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
+ return 2;
+ }
+
+ MainLoop();
+ KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
+ return 3;
+}
+
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
new file mode 100644
index 0000000..693e6cb
--- /dev/null
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <healthd/healthd.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+
+class HealthLoop {
+ public:
+ HealthLoop();
+
+ // Client is responsible for holding this forever. Process will exit
+ // when this is destroyed.
+ virtual ~HealthLoop();
+
+ // Initialize and start the main loop. This function does not exit unless
+ // the process is interrupted.
+ // Once the loop is started, event handlers are no longer allowed to be
+ // registered.
+ int StartLoop();
+
+ protected:
+ // healthd_mode_ops overrides. Note that healthd_mode_ops->battery_update
+ // is missing because it is only used by BatteryMonitor.
+ // Init is called right after epollfd_ is initialized (so RegisterEvent
+ // is allowed) but before other things are initialized (so SetChargerOnline
+ // is not allowed.)
+ virtual void Init(healthd_config* config) = 0;
+ virtual void Heartbeat() = 0;
+ virtual int PrepareToWait() = 0;
+
+ // Note that this is NOT healthd_mode_ops->battery_update(BatteryProperties*),
+ // which is called by BatteryMonitor after values are fetched. This is the
+ // implementation of healthd_battery_update(), which calls
+ // the correct IHealth::update(),
+ // which calls BatteryMonitor::update(), which calls
+ // healthd_mode_ops->battery_update(BatteryProperties*).
+ virtual void ScheduleBatteryUpdate() = 0;
+
+ // Register an epoll event. When there is an event, |func| will be
+ // called with |this| as the first argument and |epevents| as the second.
+ // This may be called in a different thread from where StartLoop is called
+ // (for obvious reasons; StartLoop never ends).
+ // Once the loop is started, event handlers are no longer allowed to be
+ // registered.
+ using BoundFunction = std::function<void(HealthLoop*, uint32_t /* epevents */)>;
+ int RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup);
+
+ // Helper for implementing ScheduleBatteryUpdate(). An implementation of
+ // ScheduleBatteryUpdate should get charger_online from BatteryMonitor::update(),
+ // then reset wake alarm interval by calling AdjustWakealarmPeriods.
+ void AdjustWakealarmPeriods(bool charger_online);
+
+ private:
+ struct EventHandler {
+ HealthLoop* object = nullptr;
+ int fd;
+ BoundFunction func;
+ };
+
+ int InitInternal();
+ void MainLoop();
+ void WakeAlarmInit();
+ void WakeAlarmEvent(uint32_t);
+ void UeventInit();
+ void UeventEvent(uint32_t);
+ void WakeAlarmSetInterval(int interval);
+ void PeriodicChores();
+
+ // These are fixed after InitInternal() is called.
+ struct healthd_config healthd_config_;
+ android::base::unique_fd wakealarm_fd_;
+ android::base::unique_fd uevent_fd_;
+
+ android::base::unique_fd epollfd_;
+ std::vector<std::unique_ptr<EventHandler>> event_handlers_;
+ int awake_poll_interval_; // -1 for no epoll timeout
+ int wakealarm_wake_interval_;
+
+ // If set to true, future RegisterEvent() will be rejected. This is to ensure all
+ // events are registered before StartLoop().
+ bool reject_event_register_ = false;
+};
+
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealthloop/include/health/utils.h b/health/utils/libhealthloop/include/health/utils.h
new file mode 100644
index 0000000..e46771c
--- /dev/null
+++ b/health/utils/libhealthloop/include/health/utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <healthd/healthd.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+
+void InitHealthdConfig(struct healthd_config* healthd_config);
+
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp
new file mode 100644
index 0000000..b0d153f
--- /dev/null
+++ b/health/utils/libhealthloop/utils.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <health/utils.h>
+namespace android {
+namespace hardware {
+namespace health {
+
+// Periodic chores fast interval in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+// Periodic chores fast interval in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+
+void InitHealthdConfig(struct healthd_config* healthd_config) {
+ *healthd_config = {
+ .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
+ .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
+ .batteryStatusPath = String8(String8::kEmptyString),
+ .batteryHealthPath = String8(String8::kEmptyString),
+ .batteryPresentPath = String8(String8::kEmptyString),
+ .batteryCapacityPath = String8(String8::kEmptyString),
+ .batteryVoltagePath = String8(String8::kEmptyString),
+ .batteryTemperaturePath = String8(String8::kEmptyString),
+ .batteryTechnologyPath = String8(String8::kEmptyString),
+ .batteryCurrentNowPath = String8(String8::kEmptyString),
+ .batteryCurrentAvgPath = String8(String8::kEmptyString),
+ .batteryChargeCounterPath = String8(String8::kEmptyString),
+ .batteryFullChargePath = String8(String8::kEmptyString),
+ .batteryCycleCountPath = String8(String8::kEmptyString),
+ .energyCounter = NULL,
+ .boot_min_cap = 0,
+ .screen_on = NULL,
+ };
+}
+
+} // namespace health
+} // namespace hardware
+} // namespace android