1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "chre/platform/system_timer.h"
18
19#include "chre/platform/log.h"
20#include "chre/util/time.h"
21
22#include <errno.h>
23#include <signal.h>
24#include <string.h>
25
26namespace chre {
27
28namespace {
29
30constexpr uint64_t kOneSecondInNanoseconds = 1000000000;
31
32void NanosecondsToTimespec(uint64_t ns, struct timespec *ts) {
33  ts->tv_sec = ns / kOneSecondInNanoseconds;
34  ts->tv_nsec = ns % kOneSecondInNanoseconds;
35}
36
37}  // anonymous namespace
38
39void SystemTimerBase::systemTimerNotifyCallback(union sigval cookie) {
40  SystemTimer *sysTimer = static_cast<SystemTimer*>(cookie.sival_ptr);
41  sysTimer->mCallback(sysTimer->mData);
42}
43
44SystemTimer::SystemTimer() {}
45
46SystemTimer::~SystemTimer() {
47  if (mInitialized) {
48    int ret = timer_delete(mTimerId);
49    if (ret != 0) {
50      LOGE("Couldn't delete timer: %s", strerror(errno));
51    }
52    mInitialized = false;
53  }
54}
55
56bool SystemTimer::init() {
57  if (mInitialized) {
58    LOGW("Tried re-initializing timer");
59  } else {
60    struct sigevent sigevt = {};
61    sigevt.sigev_notify = SIGEV_THREAD;
62    sigevt.sigev_value.sival_ptr = this;
63    sigevt.sigev_notify_function = systemTimerNotifyCallback;
64    sigevt.sigev_notify_attributes = nullptr;
65
66    int ret = timer_create(CLOCK_MONOTONIC, &sigevt, &mTimerId);
67    if (ret != 0) {
68      LOGE("Couldn't create timer: %s", strerror(errno));
69    } else {
70      mInitialized = true;
71    }
72  }
73
74  return mInitialized;
75}
76
77bool SystemTimer::set(SystemTimerCallback *callback, void *data,
78    Nanoseconds delay) {
79  // 0 has a special meaning in POSIX, i.e. cancel the timer. In our API, a
80  // value of 0 just means fire right away.
81  if (delay.toRawNanoseconds() == 0) {
82    delay = Nanoseconds(1);
83  }
84
85  if (mInitialized) {
86    mCallback = callback;
87    mData = data;
88    return setInternal(delay.toRawNanoseconds());
89  } else {
90    return false;
91  }
92}
93
94bool SystemTimer::cancel() {
95  if (mInitialized) {
96    // Setting delay to 0 disarms the timer.
97    return setInternal(0);
98  } else {
99    return false;
100  }
101}
102
103bool SystemTimer::isActive() {
104  bool isActive = false;
105  if (mInitialized) {
106    struct itimerspec spec = {};
107    int ret = timer_gettime(mTimerId, &spec);
108    if (ret != 0) {
109      LOGE("Couldn't obtain current timer configuration: %s", strerror(errno));
110    }
111
112    isActive = (spec.it_value.tv_sec > 0 || spec.it_value.tv_nsec > 0);
113  }
114
115  return isActive;
116}
117
118bool SystemTimerBase::setInternal(uint64_t delayNs) {
119  constexpr int kFlags = 0;
120  struct itimerspec spec = {};
121  bool success = false;
122
123  NanosecondsToTimespec(delayNs, &spec.it_value);
124  NanosecondsToTimespec(0, &spec.it_interval);
125
126  int ret = timer_settime(mTimerId, kFlags, &spec, nullptr);
127  if (ret != 0) {
128    LOGE("Couldn't set timer: %s", strerror(errno));
129  } else {
130    LOGD("Set timer to expire in %.f ms", (delayNs / 1000000.0));
131    success = true;
132  }
133
134  return success;
135}
136
137}  // namespace chre
138