* Copyright (C) 2021 Huawei Device Co., Ltd.
* 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 <unistd.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sstream>
#include "timer_handler.h"
#include "timer_manager_interface.h"
namespace OHOS {
namespace MiscServices {
namespace {
static constexpr uint32_t ALARM_TIME_CHANGE_MASK = 1 << 16;
constexpr int CLOCK_POWEROFF_ALARM = 12;
static constexpr clockid_t alarm_to_clock_id[N_TIMER_FDS] = {
CLOCK_REALTIME_ALARM,
CLOCK_REALTIME,
CLOCK_BOOTTIME_ALARM,
CLOCK_BOOTTIME,
CLOCK_MONOTONIC,
CLOCK_REALTIME,
#ifdef SET_AUTO_REBOOT_ENABLE
CLOCK_POWEROFF_ALARM,
#endif
};
}
std::shared_ptr<TimerHandler> TimerHandler::Create()
{
TimerFds fds;
int epollfd = epoll_create(fds.size());
if (epollfd < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "epoll_create %{public}d failed:%{public}s",
static_cast<int>(fds.size()), strerror(errno));
return nullptr;
}
fdsan_exchange_owner_tag(epollfd, 0, BASE_TIME_FDSAN_TAG);
if (!CreateTimerFds(fds)) {
fdsan_close_with_tag(epollfd, BASE_TIME_FDSAN_TAG);
return nullptr;
}
std::shared_ptr<TimerHandler> handler = std::shared_ptr<TimerHandler>(
new (std::nothrow)TimerHandler(fds, epollfd));
#ifdef SET_AUTO_REBOOT_ENABLE
for (size_t i = 0; i < fds.size() - 1; i++) {
#else
for (size_t i = 0; i < fds.size(); i++) {
#endif
epoll_event event {};
event.events = EPOLLIN | EPOLLWAKEUP;
event.data.u32 = i;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event) < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "epoll_ctl(EPOLL_CTL_ADD) failed:%{public}s", strerror(errno));
return nullptr;
}
}
if (SetRealTimeFd(fds) < 0 && errno != ECANCELED) {
return nullptr;
}
return handler;
}
int TimerHandler::SetRealTimeFd(TimerFds fds)
{
itimerspec spec {};
#ifdef SET_AUTO_REBOOT_ENABLE
int err = timerfd_settime(fds[ALARM_TYPE_COUNT - 1], TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, nullptr);
if (err) {
TIME_HILOGW(TIME_MODULE_SERVICE, "settime fd:%{public}d, res:%{public}d, errno:%{public}s",
fds[ALARM_TYPE_COUNT - 1], err, strerror(errno));
}
#else
int err = timerfd_settime(fds[ALARM_TYPE_COUNT], TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, nullptr);
if (err) {
TIME_HILOGW(TIME_MODULE_SERVICE, "settime fd:%{public}d, res:%{public}d, errno:%{public}s",
fds[ALARM_TYPE_COUNT], err, strerror(errno));
}
#endif
return err;
}
bool TimerHandler::CreateTimerFds(TimerFds &fds) {
std::string fdStr = "";
std::string typStr = "";
for (size_t i = 0; i < fds.size(); i++) {
if (alarm_to_clock_id[i] != CLOCK_POWEROFF_ALARM) {
fds[i] = timerfd_create(alarm_to_clock_id[i], 0);
} else {
fds[i] = timerfd_create(CLOCK_POWEROFF_ALARM, TFD_NONBLOCK);
}
if (fds[i] < 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "timerfd_create %{public}d failed:%{public}s",
static_cast<int>(i), strerror(errno));
for (size_t j = 0; j < i; j++) {
fdsan_close_with_tag(fds[j], BASE_TIME_FDSAN_TAG);
}
return false;
}
fdsan_exchange_owner_tag(fds[i], 0, BASE_TIME_FDSAN_TAG);
fdStr += std::to_string(fds[i]) + " ";
typStr += std::to_string(i) + " ";
}
TIME_HILOGW(TIME_MODULE_SERVICE, "create fd:[%{public}s], typ:[%{public}s]", fdStr.c_str(), typStr.c_str());
return true;
}
TimerHandler::TimerHandler(const TimerFds &fds, int epollfd)
: fds_ {fds}, epollFd_ {epollfd}
{
}
TimerHandler::~TimerHandler()
{
for (auto fd : fds_) {
epoll_ctl(epollFd_, EPOLL_CTL_DEL, fd, nullptr);
fdsan_close_with_tag(fd, BASE_TIME_FDSAN_TAG);
}
fdsan_close_with_tag(epollFd_, BASE_TIME_FDSAN_TAG);
}
int TimerHandler::Set(uint32_t type, std::chrono::nanoseconds when, std::chrono::steady_clock::time_point bootTime)
{
if (static_cast<size_t>(type) > ALARM_TYPE_COUNT) {
errno = EINVAL;
return -1;
}
auto second = std::chrono::duration_cast<std::chrono::seconds>(when);
auto milliSecond = std::chrono::duration_cast<std::chrono::milliseconds>(when);
auto bootTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(bootTime.time_since_epoch());
if (type == static_cast<uint32_t>(ITimerManager::TimerType::ELAPSED_REALTIME_WAKEUP)) {
TIME_SIMPLIFY_HILOGW(TIME_MODULE_SERVICE, "t%{public}d %{public}lld bt:%{public}lld", type,
milliSecond.count(), bootTimeMs.count());
} else {
TIME_SIMPLIFY_HILOGW(TIME_MODULE_SERVICE, "t%{public}d %{public}lld", type, milliSecond.count());
}
timespec ts {second.count(), (when - second).count()};
itimerspec spec {timespec {}, ts};
int ret = timerfd_settime(fds_[type], TFD_TIMER_ABSTIME, &spec, nullptr);
if (ret != 0) {
TIME_HILOGE(TIME_MODULE_SERVICE, "Set timer to kernel ret:%{public}d error:%{public}s",
ret, strerror(errno));
}
return ret;
}
uint32_t TimerHandler::WaitForAlarm()
{
epoll_event events[N_TIMER_FDS];
int nevents = 0;
do {
nevents = epoll_wait(epollFd_, events, N_TIMER_FDS, -1);
} while (nevents < 0 && errno == EINTR);
uint32_t result = 0;
for (int i = 0; i < nevents; i++) {
uint32_t alarm_idx = events[i].data.u32;
uint64_t unused;
ssize_t err = read(fds_[alarm_idx], &unused, sizeof(unused));
if (err < 0) {
if (alarm_idx == ALARM_TYPE_COUNT && errno == ECANCELED) {
result |= ALARM_TIME_CHANGE_MASK;
} else {
return err;
}
} else {
result |= (1 << alarm_idx);
}
}
return result;
}
}
}