Loading health/2.0/default/Android.bp +5 −1 Original line number Diff line number Diff line Loading @@ -31,7 +31,11 @@ cc_library_static { vendor_available: true, srcs: [ "Health.cpp", "healthd_common.cpp", "healthd_common_adapter.cpp", ], whole_static_libs: [ "libhealthloop", ], export_include_dirs: ["include"], Loading health/2.0/default/healthd_common_adapter.cpp 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * 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; } health/utils/libhealthloop/Android.bp 0 → 100644 +35 −0 Original line number Diff line number Diff line // 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", ], } health/2.0/default/healthd_common.cpp→health/utils/libhealthloop/HealthLoop.cpp +267 −0 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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; Loading @@ -110,47 +94,46 @@ static void wakealarm_set_interval(int interval) { 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 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(); : healthd_config_.periodic_chores_interval_fast * 1000; } static void periodic_chores() { healthd_battery_update(); void HealthLoop::PeriodicChores() { ScheduleBatteryUpdate(); } // 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; Loading @@ -161,7 +144,7 @@ static void uevent_event(uint32_t /*epevents*/) { while (*cp) { if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { healthd_battery_update(); ScheduleBatteryUpdate(); break; } Loading @@ -171,58 +154,63 @@ static void uevent_event(uint32_t /*epevents*/) { } } 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"); Loading @@ -230,44 +218,50 @@ static void healthd_mainloop(void) { } 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 health/utils/libhealthloop/include/health/HealthLoop.h 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * 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 Loading
health/2.0/default/Android.bp +5 −1 Original line number Diff line number Diff line Loading @@ -31,7 +31,11 @@ cc_library_static { vendor_available: true, srcs: [ "Health.cpp", "healthd_common.cpp", "healthd_common_adapter.cpp", ], whole_static_libs: [ "libhealthloop", ], export_include_dirs: ["include"], Loading
health/2.0/default/healthd_common_adapter.cpp 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * 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; }
health/utils/libhealthloop/Android.bp 0 → 100644 +35 −0 Original line number Diff line number Diff line // 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", ], }
health/2.0/default/healthd_common.cpp→health/utils/libhealthloop/HealthLoop.cpp +267 −0 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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; Loading @@ -110,47 +94,46 @@ static void wakealarm_set_interval(int interval) { 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 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(); : healthd_config_.periodic_chores_interval_fast * 1000; } static void periodic_chores() { healthd_battery_update(); void HealthLoop::PeriodicChores() { ScheduleBatteryUpdate(); } // 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; Loading @@ -161,7 +144,7 @@ static void uevent_event(uint32_t /*epevents*/) { while (*cp) { if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { healthd_battery_update(); ScheduleBatteryUpdate(); break; } Loading @@ -171,58 +154,63 @@ static void uevent_event(uint32_t /*epevents*/) { } } 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"); Loading @@ -230,44 +218,50 @@ static void healthd_mainloop(void) { } 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
health/utils/libhealthloop/include/health/HealthLoop.h 0 → 100644 +108 −0 Original line number Diff line number Diff line /* * 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