1 /*
2  * Copyright 2024 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 #define LOG_TAG "InputTracer"
18 
19 #include "ThreadedBackend.h"
20 
21 #include "InputTracingPerfettoBackend.h"
22 
23 #include <android-base/logging.h>
24 
25 namespace android::inputdispatcher::trace::impl {
26 
27 namespace {
28 
29 // Helper to std::visit with lambdas.
30 template <typename... V>
31 struct Visitor : V... {
32     using V::operator()...;
33 };
34 
35 } // namespace
36 
37 // --- ThreadedBackend ---
38 
39 template <typename Backend>
ThreadedBackend(Backend && innerBackend)40 ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend)
41       : mBackend(std::move(innerBackend)),
42         mTracerThread(
43                 "InputTracer", [this]() { threadLoop(); },
__anon69a344ec0302() 44                 [this]() { mThreadWakeCondition.notify_all(); }) {}
45 
46 template <typename Backend>
~ThreadedBackend()47 ThreadedBackend<Backend>::~ThreadedBackend() {
48     {
49         std::scoped_lock lock(mLock);
50         mThreadExit = true;
51     }
52     mThreadWakeCondition.notify_all();
53 }
54 
55 template <typename Backend>
traceMotionEvent(const TracedMotionEvent & event,const TracedEventMetadata & metadata)56 void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event,
57                                                 const TracedEventMetadata& metadata) {
58     std::scoped_lock lock(mLock);
59     mQueue.emplace_back(event, metadata);
60     setIdleStatus(false);
61     mThreadWakeCondition.notify_all();
62 }
63 
64 template <typename Backend>
traceKeyEvent(const TracedKeyEvent & event,const TracedEventMetadata & metadata)65 void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event,
66                                              const TracedEventMetadata& metadata) {
67     std::scoped_lock lock(mLock);
68     mQueue.emplace_back(event, metadata);
69     setIdleStatus(false);
70     mThreadWakeCondition.notify_all();
71 }
72 
73 template <typename Backend>
traceWindowDispatch(const WindowDispatchArgs & dispatchArgs,const TracedEventMetadata & metadata)74 void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
75                                                    const TracedEventMetadata& metadata) {
76     std::scoped_lock lock(mLock);
77     mQueue.emplace_back(dispatchArgs, metadata);
78     setIdleStatus(false);
79     mThreadWakeCondition.notify_all();
80 }
81 
82 template <typename Backend>
threadLoop()83 void ThreadedBackend<Backend>::threadLoop() {
84     std::vector<TraceEntry> entries;
85 
86     { // acquire lock
87         std::unique_lock lock(mLock);
88         base::ScopedLockAssertion assumeLocked(mLock);
89 
90         if (mQueue.empty()) {
91             setIdleStatus(true);
92         }
93 
94         // Wait until we need to process more events or exit.
95         mThreadWakeCondition.wait(lock,
96                                   [&]() REQUIRES(mLock) { return mThreadExit || !mQueue.empty(); });
97         if (mThreadExit) {
98             setIdleStatus(true);
99             return;
100         }
101 
102         mQueue.swap(entries);
103     } // release lock
104 
105     // Trace the events into the backend without holding the lock to reduce the amount of
106     // work performed in the critical section.
107     for (const auto& [entry, traceArgs] : entries) {
108         std::visit(Visitor{[&](const TracedMotionEvent& e) {
109                                mBackend.traceMotionEvent(e, traceArgs);
110                            },
111                            [&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e, traceArgs); },
112                            [&](const WindowDispatchArgs& args) {
113                                mBackend.traceWindowDispatch(args, traceArgs);
114                            }},
115                    entry);
116     }
117     entries.clear();
118 }
119 
120 template <typename Backend>
getIdleWaiterForTesting()121 std::function<void()> ThreadedBackend<Backend>::getIdleWaiterForTesting() {
122     std::scoped_lock lock(mLock);
123     if (!mIdleWaiter) {
124         mIdleWaiter = std::make_shared<IdleWaiter>();
125     }
126 
127     // Return a lambda that holds a strong reference to the idle waiter, whose lifetime can extend
128     // beyond this threaded backend object.
129     return [idleWaiter = mIdleWaiter]() {
130         std::unique_lock idleLock(idleWaiter->idleLock);
131         base::ScopedLockAssertion assumeLocked(idleWaiter->idleLock);
132         idleWaiter->threadIdleCondition.wait(idleLock, [&]() REQUIRES(idleWaiter->idleLock) {
133             return idleWaiter->isIdle;
134         });
135     };
136 }
137 
138 template <typename Backend>
setIdleStatus(bool isIdle)139 void ThreadedBackend<Backend>::setIdleStatus(bool isIdle) {
140     if (!mIdleWaiter) {
141         return;
142     }
143     std::scoped_lock idleLock(mIdleWaiter->idleLock);
144     mIdleWaiter->isIdle = isIdle;
145     if (isIdle) {
146         mIdleWaiter->threadIdleCondition.notify_all();
147     }
148 }
149 
150 // Explicit template instantiation for the PerfettoBackend.
151 template class ThreadedBackend<PerfettoBackend>;
152 
153 } // namespace android::inputdispatcher::trace::impl
154