| /* |
| * Copyright 2023 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 "TimerProvider.h" |
| |
| #include <chrono> |
| #include <string> |
| |
| #include <android-base/logging.h> |
| #include <input/PrintTools.h> |
| |
| namespace android { |
| |
| namespace { |
| |
| nsecs_t stimeToNsecs(stime_t time) { |
| return std::chrono::duration_cast<std::chrono::nanoseconds>( |
| std::chrono::duration<stime_t>(time)) |
| .count(); |
| } |
| |
| stime_t nsecsToStime(nsecs_t time) { |
| return std::chrono::duration_cast<std::chrono::duration<stime_t>>( |
| std::chrono::nanoseconds(time)) |
| .count(); |
| } |
| |
| GesturesTimer* createTimer(void* data) { |
| return static_cast<TimerProvider*>(data)->createTimer(); |
| } |
| |
| void setDeadline(void* data, GesturesTimer* timer, stime_t delay, GesturesTimerCallback callback, |
| void* callbackData) { |
| static_cast<TimerProvider*>(data)->setDeadline(timer, stimeToNsecs(delay), callback, |
| callbackData); |
| }; |
| |
| void cancelTimer(void* data, GesturesTimer* timer) { |
| static_cast<TimerProvider*>(data)->cancelTimer(timer); |
| } |
| |
| void freeTimer(void* data, GesturesTimer* timer) { |
| static_cast<TimerProvider*>(data)->freeTimer(timer); |
| } |
| |
| } // namespace |
| |
| const GesturesTimerProvider kGestureTimerProvider = { |
| .create_fn = createTimer, |
| .set_fn = setDeadline, |
| .cancel_fn = cancelTimer, |
| .free_fn = freeTimer, |
| }; |
| |
| TimerProvider::TimerProvider(InputReaderContext& context) : mReaderContext(context) {} |
| |
| std::string TimerProvider::dump() { |
| std::string dump; |
| auto timerPtrToString = [](const std::unique_ptr<GesturesTimer>& timer) { |
| return std::to_string(timer->id); |
| }; |
| dump += "Timer IDs: " + dumpVector<std::unique_ptr<GesturesTimer>>(mTimers, timerPtrToString) + |
| "\n"; |
| dump += "Deadlines and corresponding timer IDs:\n"; |
| dump += addLinePrefix(dumpMap(mDeadlines, constToString, |
| [](const Deadline& deadline) { |
| return std::to_string(deadline.timerId); |
| }), |
| " ") + |
| "\n"; |
| return dump; |
| } |
| |
| void TimerProvider::triggerCallbacks(nsecs_t when) { |
| while (!mDeadlines.empty() && when >= mDeadlines.begin()->first) { |
| const auto& deadlinePair = mDeadlines.begin(); |
| deadlinePair->second.callback(when); |
| mDeadlines.erase(deadlinePair); |
| } |
| requestTimeout(); |
| } |
| |
| GesturesTimer* TimerProvider::createTimer() { |
| mTimers.push_back(std::make_unique<GesturesTimer>()); |
| mTimers.back()->id = mNextTimerId; |
| mNextTimerId++; |
| return mTimers.back().get(); |
| } |
| |
| void TimerProvider::setDeadline(GesturesTimer* timer, nsecs_t delay, GesturesTimerCallback callback, |
| void* callbackData) { |
| setDeadlineWithoutRequestingTimeout(timer, delay, callback, callbackData); |
| requestTimeout(); |
| } |
| |
| void TimerProvider::setDeadlineWithoutRequestingTimeout(GesturesTimer* timer, nsecs_t delay, |
| GesturesTimerCallback callback, |
| void* callbackData) { |
| const nsecs_t now = getCurrentTime(); |
| const nsecs_t time = now + delay; |
| std::function<void(nsecs_t)> wrappedCallback = [=, this](nsecs_t triggerTime) { |
| stime_t nextDelay = callback(nsecsToStime(triggerTime), callbackData); |
| if (nextDelay >= 0.0) { |
| // When rescheduling a deadline, we know that we're running inside a call to |
| // triggerCallbacks, at the end of which requestTimeout will be called. This means that |
| // we don't want to call the public setDeadline, as that will request a timeout before |
| // triggerCallbacks has removed this current deadline, resulting in a request for a |
| // timeout that has already passed. |
| setDeadlineWithoutRequestingTimeout(timer, stimeToNsecs(nextDelay), callback, |
| callbackData); |
| } |
| }; |
| mDeadlines.insert({time, Deadline(wrappedCallback, timer->id)}); |
| } |
| |
| void TimerProvider::cancelTimer(GesturesTimer* timer) { |
| int id = timer->id; |
| std::erase_if(mDeadlines, [id](const auto& item) { return item.second.timerId == id; }); |
| requestTimeout(); |
| } |
| |
| void TimerProvider::freeTimer(GesturesTimer* timer) { |
| cancelTimer(timer); |
| std::erase_if(mTimers, [timer](std::unique_ptr<GesturesTimer>& t) { return t.get() == timer; }); |
| } |
| |
| void TimerProvider::requestTimeout() { |
| if (!mDeadlines.empty()) { |
| // Because a std::multimap is sorted by key, we simply use the time for the first entry. |
| mReaderContext.requestTimeoutAtTime(mDeadlines.begin()->first); |
| } |
| } |
| |
| nsecs_t TimerProvider::getCurrentTime() { |
| return systemTime(SYSTEM_TIME_MONOTONIC); |
| } |
| |
| } // namespace android |