blob: df2f260098ac4a2d2465e4516c68bb4d50f87ab3 [file] [log] [blame]
/*
* 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