blob: 4f618851623408a3cf0f0333a06dfb95ddddc3ed [file] [log] [blame]
/*
* Copyright 2024 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.
*/
#define LOG_TAG "InputTracer"
#include "ThreadedBackend.h"
#include "InputTracingPerfettoBackend.h"
#include <android-base/logging.h>
namespace android::inputdispatcher::trace::impl {
namespace {
// Helper to std::visit with lambdas.
template <typename... V>
struct Visitor : V... {
using V::operator()...;
};
} // namespace
// --- ThreadedBackend ---
template <typename Backend>
ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend)
: mBackend(std::move(innerBackend)),
mTracerThread(
"InputTracer", [this]() { threadLoop(); },
[this]() { mThreadWakeCondition.notify_all(); }, /*isInCriticalPath=*/false) {}
template <typename Backend>
ThreadedBackend<Backend>::~ThreadedBackend() {
{
std::scoped_lock lock(mLock);
mThreadExit = true;
}
mThreadWakeCondition.notify_all();
}
template <typename Backend>
void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event,
const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
mQueue.emplace_back(event, metadata);
setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event,
const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
mQueue.emplace_back(event, metadata);
setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
mQueue.emplace_back(dispatchArgs, metadata);
setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
void ThreadedBackend<Backend>::threadLoop() {
std::vector<TraceEntry> entries;
{ // acquire lock
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
if (mQueue.empty()) {
setIdleStatus(true);
}
// Wait until we need to process more events or exit.
mThreadWakeCondition.wait(lock,
[&]() REQUIRES(mLock) { return mThreadExit || !mQueue.empty(); });
if (mThreadExit) {
setIdleStatus(true);
return;
}
mQueue.swap(entries);
} // release lock
// Trace the events into the backend without holding the lock to reduce the amount of
// work performed in the critical section.
for (const auto& [entry, traceArgs] : entries) {
std::visit(Visitor{[&](const TracedMotionEvent& e) {
mBackend.traceMotionEvent(e, traceArgs);
},
[&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e, traceArgs); },
[&](const WindowDispatchArgs& args) {
mBackend.traceWindowDispatch(args, traceArgs);
}},
entry);
}
entries.clear();
}
template <typename Backend>
std::function<void()> ThreadedBackend<Backend>::getIdleWaiterForTesting() {
std::scoped_lock lock(mLock);
if (!mIdleWaiter) {
mIdleWaiter = std::make_shared<IdleWaiter>();
}
// Return a lambda that holds a strong reference to the idle waiter, whose lifetime can extend
// beyond this threaded backend object.
return [idleWaiter = mIdleWaiter]() {
std::unique_lock idleLock(idleWaiter->idleLock);
base::ScopedLockAssertion assumeLocked(idleWaiter->idleLock);
idleWaiter->threadIdleCondition.wait(idleLock, [&]() REQUIRES(idleWaiter->idleLock) {
return idleWaiter->isIdle;
});
};
}
template <typename Backend>
void ThreadedBackend<Backend>::setIdleStatus(bool isIdle) {
if (!mIdleWaiter) {
return;
}
std::scoped_lock idleLock(mIdleWaiter->idleLock);
mIdleWaiter->isIdle = isIdle;
if (isIdle) {
mIdleWaiter->threadIdleCondition.notify_all();
}
}
// Explicit template instantiation for the PerfettoBackend.
template class ThreadedBackend<PerfettoBackend>;
} // namespace android::inputdispatcher::trace::impl