[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