blob: ee89ec05b3f48fd9cf41148fad953f5819f3fcfa [file] [log] [blame]
/**
* Copyright (c) 2021, 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 "libsysfsmonitor"
#define DEBUG false
#include "SysfsMonitor.h"
#include <android-base/stringprintf.h>
#include <log/log.h>
#include <sys/epoll.h>
namespace {
using ::android::base::Error;
using ::android::base::Result;
using ::android::base::StringPrintf;
// The maximum number of sysfs files to monitor.
constexpr int32_t EPOLL_MAX_EVENTS = 10;
} // namespace
namespace android {
namespace automotive {
Result<void> SysfsMonitor::init(CallbackFunc callback) {
if (mEpollFd >= 0) {
return Error() << "Epoll instance was already created";
}
if (mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC)); mEpollFd < 0) {
return Error() << "Cannot create epoll instance: errno = " << errno;
}
mCallback = callback;
return {};
}
Result<void> SysfsMonitor::release() {
if (mEpollFd < 0) {
return Error() << "Epoll instance wasn't created";
}
for (const int32_t fd : mMonitoringFds) {
if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, /*event=*/nullptr)) {
ALOGW("Failed to deregister fd(%d) from epoll instance: errno = %d", fd, errno);
}
}
mMonitoringFds.clear();
mEpollFd.reset();
mCallback = nullptr;
return {};
}
Result<void> SysfsMonitor::registerFd(int32_t fd) {
if (fd < 0) {
return Error() << StringPrintf("fd(%d) is invalid", fd);
}
if (mMonitoringFds.count(fd) > 0) {
return Error() << StringPrintf("fd(%d) is already being monitored", fd);
}
if (mMonitoringFds.size() == EPOLL_MAX_EVENTS) {
return Error() << "Cannot monitor more than " << EPOLL_MAX_EVENTS << " sysfs files";
}
struct epoll_event eventItem = {};
eventItem.events = EPOLLIN | EPOLLPRI | EPOLLET;
eventItem.data.fd = fd;
if (int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem); result != 0) {
return Error() << StringPrintf("Failed to add fd(%d) to epoll instance: errno = %d", fd,
errno);
}
mMonitoringFds.insert(fd);
return {};
}
Result<void> SysfsMonitor::unregisterFd(int32_t fd) {
if (fd < 0) {
return Error() << StringPrintf("fd(%d) is invalid", fd);
}
if (mMonitoringFds.count(fd) == 0) {
return Error() << StringPrintf("fd(%d) is not being monitored", fd);
}
// Even when epoll_ctl() fails, we proceed to handle the request.
if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, /*event=*/nullptr)) {
ALOGW("Failed to deregister fd(%d) from epoll instance: errno = %d", fd, errno);
}
mMonitoringFds.erase(fd);
return {};
}
Result<void> SysfsMonitor::observe() {
if (mEpollFd < 0) {
return Error() << "Epoll instance is not initialized";
}
struct epoll_event events[EPOLL_MAX_EVENTS];
while (true) {
int pollResult = epoll_wait(mEpollFd, events, EPOLL_MAX_EVENTS, /*timeout=*/-1);
if (pollResult < 0) {
ALOGW("Polling sysfs failed, but continue polling: errno = %d", errno);
continue;
}
std::vector<int32_t> fds;
for (int i = 0; i < pollResult; i++) {
int fd = events[i].data.fd;
if (mMonitoringFds.count(fd) == 0) {
continue;
}
if (events[i].events & EPOLLIN) {
fds.push_back(fd);
} else if (events[i].events & EPOLLERR) {
ALOGW("An error occurred when polling fd(%d)", fd);
}
}
if (mCallback && fds.size() > 0) {
mCallback(fds);
}
}
return {};
}
} // namespace automotive
} // namespace android