blob: 8dec695df149957c517d8a09e096842721149780 [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.
*/
#include "RecurrentTimer.h"
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/SystemClock.h>
#include <inttypes.h>
#include <math.h>
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace {
using ::android::base::ScopedLockAssertion;
constexpr int INVALID_ID = -1;
} // namespace
RecurrentTimer::RecurrentTimer() {
mHandler = sp<RecurrentMessageHandler>::make(this);
mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
mThread = std::thread([this] {
Looper::setForThread(mLooper);
while (!mStopRequested) {
mLooper->pollOnce(/*timeoutMillis=*/-1);
}
});
}
RecurrentTimer::~RecurrentTimer() {
mStopRequested = true;
mLooper->removeMessages(mHandler);
mLooper->wake();
if (mThread.joinable()) {
mThread.join();
}
}
int RecurrentTimer::getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback) {
const auto& it = mIdByCallback.find(callback);
if (it != mIdByCallback.end()) {
return it->second;
}
return INVALID_ID;
}
void RecurrentTimer::registerTimerCallback(int64_t intervalInNanos,
std::shared_ptr<RecurrentTimer::Callback> callback) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
int callbackId = getCallbackIdLocked(callback);
if (callbackId == INVALID_ID) {
callbackId = mCallbackId++;
mIdByCallback.insert({callback, callbackId});
} else {
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
" ns, new: %" PRId64 " ns",
mCallbackInfoById[callbackId]->intervalInNanos, intervalInNanos);
mLooper->removeMessages(mHandler, callbackId);
}
// Aligns the nextTime to multiply of interval.
int64_t nextTimeInNanos = ceil(uptimeNanos() / intervalInNanos) * intervalInNanos;
std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
info->callback = callback;
info->intervalInNanos = intervalInNanos;
info->nextTimeInNanos = nextTimeInNanos;
mCallbackInfoById.insert({callbackId, std::move(info)});
mLooper->sendMessageAtTime(nextTimeInNanos, mHandler, Message(callbackId));
}
}
void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
int callbackId = getCallbackIdLocked(callback);
if (callbackId == INVALID_ID) {
ALOGE("No event found to unregister");
return;
}
mLooper->removeMessages(mHandler, callbackId);
mCallbackInfoById.erase(callbackId);
mIdByCallback.erase(callback);
}
}
void RecurrentTimer::handleMessage(const Message& message) {
std::shared_ptr<RecurrentTimer::Callback> callback;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
int callbackId = message.what;
auto it = mCallbackInfoById.find(callbackId);
if (it == mCallbackInfoById.end()) {
ALOGW("The event for callback ID: %d is outdated, ignore", callbackId);
return;
}
CallbackInfo* callbackInfo = it->second.get();
callback = callbackInfo->callback;
int64_t nowNanos = uptimeNanos();
// intervalCount is the number of interval we have to advance until we pass now.
size_t intervalCount =
(nowNanos - callbackInfo->nextTimeInNanos) / callbackInfo->intervalInNanos + 1;
callbackInfo->nextTimeInNanos += intervalCount * callbackInfo->intervalInNanos;
mLooper->sendMessageAtTime(callbackInfo->nextTimeInNanos, mHandler, Message(callbackId));
}
(*callback)();
}
void RecurrentMessageHandler::handleMessage(const Message& message) {
mTimer->handleMessage(message);
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android