[REFACTOR] health 2.0: healthd_common -> libhealthloop

This change converts the original healthd_common.cpp to a
C++ class, HealthLoop, that manages the infinite loop in health-related
modules (charger, health HAL, healthd, etc.). By doing so, the global
static variables (including FDs and healthd_mode_ops) are cleaned up.

This helps us implement health HAL 2.1.

In order to support legacy modules (namely, healthd, charger, health HAL
2.0, which all depends on android.hardware.health@2.0-impl), a
healthd_common_adapter.cpp file is added to
android.hardware.health@2.0-impl, so that the following functions
in healthd/healthd.h continues to be implemented using the global
healthd_mode_ops:
    - healthd_register_event
    - healthd_battery_update_internal

Test: boot up the device and run VTS test on health HAL.
Bug: 137670450
Bug: 142260281
Change-Id: Iadcfc1315155404a3600f0e1219b5bc370d96409
diff --git a/health/2.0/default/Android.bp b/health/2.0/default/Android.bp
index a85a704..ead560b 100644
--- a/health/2.0/default/Android.bp
+++ b/health/2.0/default/Android.bp
@@ -33,7 +33,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_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
index b5fdc8e..3f4b5bc 100644
--- a/health/utils/libhealthloop/HealthLoop.cpp
+++ b/health/utils/libhealthloop/HealthLoop.cpp
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "android.hardware.health@2.0-impl"
+#define LOG_TAG "HealthLoop"
 #define KLOG_LEVEL 6
 
-#include <healthd/BatteryMonitor.h>
-#include <healthd/healthd.h>
+#include <health/HealthLoop.h>
 
-#include <batteryservice/BatteryService.h>
-#include <cutils/klog.h>
-#include <cutils/uevent.h>
 #include <errno.h>
 #include <libgen.h>
 #include <stdio.h>
@@ -31,77 +27,65 @@
 #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 <health2/Health.h>
+#include <health/utils.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;
+using namespace std::chrono_literals;
 
 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
 
-static int uevent_fd;
-static int wakealarm_fd;
+namespace android {
+namespace hardware {
+namespace health {
 
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
+HealthLoop::HealthLoop() {
+    InitHealthdConfig(&healthd_config_);
+    awake_poll_interval_ = -1;
+    wakealarm_wake_interval_ = healthd_config_.periodic_chores_interval_fast;
+}
 
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
+HealthLoop::~HealthLoop() {
+    LOG(FATAL) << "HealthLoop cannot be destroyed";
+}
 
-using ::android::hardware::health::V2_0::implementation::Health;
+int HealthLoop::RegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) {
+    CHECK(!reject_event_register_);
 
-struct healthd_mode_ops* healthd_mode_ops = nullptr;
+    auto* event_handler =
+            event_handlers_
+                    .emplace_back(std::make_unique<EventHandler>(EventHandler{this, fd, func}))
+                    .get();
 
-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) {
+    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;
     }
 
-    eventct++;
     return 0;
 }
 
-static void wakealarm_set_interval(int interval) {
+void HealthLoop::WakeAlarmSetInterval(int interval) {
     struct itimerspec itval;
 
-    if (wakealarm_fd == -1) return;
+    if (wakealarm_fd_ == -1) return;
 
-    wakealarm_wake_interval = interval;
+    wakealarm_wake_interval_ = interval;
 
     if (interval == -1) interval = 0;
 
@@ -110,47 +94,46 @@
     itval.it_value.tv_sec = interval;
     itval.it_value.tv_nsec = 0;
 
-    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
+    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) {
+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;
+    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);
+    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;
+    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;
+        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();
+void HealthLoop::PeriodicChores() {
+    ScheduleBatteryUpdate();
 }
 
-static void periodic_chores() {
-    healthd_battery_update();
-}
-
+// TODO(b/140330870): Use BPF instead.
 #define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
+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);
+    n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
     if (n <= 0) return;
     if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
         return;
@@ -161,7 +144,7 @@
 
     while (*cp) {
         if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
-            healthd_battery_update();
+            ScheduleBatteryUpdate();
             break;
         }
 
@@ -171,58 +154,63 @@
     }
 }
 
-static void uevent_init(void) {
-    uevent_fd = uevent_open_socket(64 * 1024, true);
+void HealthLoop::UeventInit(void) {
+    uevent_fd_.reset(uevent_open_socket(64 * 1024, true));
 
-    if (uevent_fd < 0) {
+    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))
+    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");
 }
 
-static void wakealarm_event(uint32_t /*epevents*/) {
+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) {
+    if (read(wakealarm_fd_, &wakeups, sizeof(wakeups)) == -1) {
         KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
         return;
     }
 
-    periodic_chores();
+    PeriodicChores();
 }
 
-static void wakealarm_init(void) {
-    wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
-    if (wakealarm_fd == -1) {
+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 (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
+    if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
         KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");
 
-    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
+    WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
 }
 
-static void healthd_mainloop(void) {
+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 timeout = awake_poll_interval_;
+
         int mode_timeout;
 
         /* Don't wait for first timer timeout to run periodic chores */
-        if (!nevents) periodic_chores();
+        if (!nevents) PeriodicChores();
 
-        healthd_mode_ops->heartbeat();
+        Heartbeat();
 
-        mode_timeout = healthd_mode_ops->preparetowait();
+        mode_timeout = PrepareToWait();
         if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;
-        nevents = epoll_wait(epollfd, events, eventct, 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");
@@ -230,44 +218,50 @@
         }
 
         for (int n = 0; n < nevents; ++n) {
-            if (events[n].data.ptr) (*(void (*)(int))events[n].data.ptr)(events[n].events);
+            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;
 }
 
-static int healthd_init() {
-    epollfd = epoll_create1(EPOLL_CLOEXEC);
-    if (epollfd == -1) {
+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;
     }
 
-    healthd_mode_ops->init(&healthd_config);
-    wakealarm_init();
-    uevent_init();
+    // 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 healthd_main() {
+int HealthLoop::StartLoop() {
     int ret;
 
     klog_set_level(KLOG_LEVEL);
 
-    if (!healthd_mode_ops) {
-        KLOG_ERROR("healthd ops not set, exiting\n");
-        exit(1);
-    }
-
-    ret = healthd_init();
+    ret = InitInternal();
     if (ret) {
-        KLOG_ERROR("Initialization failed, exiting\n");
-        exit(2);
+        KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
+        return 2;
     }
 
-    healthd_mainloop();
-    KLOG_ERROR("Main loop terminated, exiting\n");
+    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