1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2024 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker *
4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker *
8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker *
10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker */
16*38e8c45fSAndroid Build Coastguard Worker
17*38e8c45fSAndroid Build Coastguard Worker #include "InputTraceSession.h"
18*38e8c45fSAndroid Build Coastguard Worker
19*38e8c45fSAndroid Build Coastguard Worker #include <NotifyArgsBuilders.h>
20*38e8c45fSAndroid Build Coastguard Worker #include <android-base/logging.h>
21*38e8c45fSAndroid Build Coastguard Worker #include <gtest/gtest.h>
22*38e8c45fSAndroid Build Coastguard Worker #include <input/PrintTools.h>
23*38e8c45fSAndroid Build Coastguard Worker #include <perfetto/trace/android/android_input_event.pbzero.h>
24*38e8c45fSAndroid Build Coastguard Worker #include <perfetto/trace/android/winscope_extensions.pbzero.h>
25*38e8c45fSAndroid Build Coastguard Worker #include <perfetto/trace/android/winscope_extensions_impl.pbzero.h>
26*38e8c45fSAndroid Build Coastguard Worker
27*38e8c45fSAndroid Build Coastguard Worker #include <utility>
28*38e8c45fSAndroid Build Coastguard Worker
29*38e8c45fSAndroid Build Coastguard Worker namespace android {
30*38e8c45fSAndroid Build Coastguard Worker
31*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::AndroidInputEvent;
32*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::AndroidInputEventConfig;
33*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::AndroidKeyEvent;
34*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::AndroidMotionEvent;
35*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent;
36*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::WinscopeExtensions;
37*38e8c45fSAndroid Build Coastguard Worker using perfetto::protos::pbzero::WinscopeExtensionsImpl;
38*38e8c45fSAndroid Build Coastguard Worker
39*38e8c45fSAndroid Build Coastguard Worker // These operator<< definitions must be in the global namespace for them to be accessible to the
40*38e8c45fSAndroid Build Coastguard Worker // GTEST library. They cannot be in the anonymous namespace.
operator <<(std::ostream & out,const std::variant<KeyEvent,MotionEvent> & event)41*38e8c45fSAndroid Build Coastguard Worker static std::ostream& operator<<(std::ostream& out,
42*38e8c45fSAndroid Build Coastguard Worker const std::variant<KeyEvent, MotionEvent>& event) {
43*38e8c45fSAndroid Build Coastguard Worker std::visit([&](const auto& e) { out << e; }, event);
44*38e8c45fSAndroid Build Coastguard Worker return out;
45*38e8c45fSAndroid Build Coastguard Worker }
46*38e8c45fSAndroid Build Coastguard Worker
operator <<(std::ostream & out,const InputTraceSession::WindowDispatchEvent & event)47*38e8c45fSAndroid Build Coastguard Worker static std::ostream& operator<<(std::ostream& out,
48*38e8c45fSAndroid Build Coastguard Worker const InputTraceSession::WindowDispatchEvent& event) {
49*38e8c45fSAndroid Build Coastguard Worker out << "Window dispatch to windowId: " << event.window->getId() << ", event: " << event.event;
50*38e8c45fSAndroid Build Coastguard Worker return out;
51*38e8c45fSAndroid Build Coastguard Worker }
52*38e8c45fSAndroid Build Coastguard Worker
53*38e8c45fSAndroid Build Coastguard Worker namespace {
54*38e8c45fSAndroid Build Coastguard Worker
getId(const std::variant<KeyEvent,MotionEvent> & event)55*38e8c45fSAndroid Build Coastguard Worker inline uint32_t getId(const std::variant<KeyEvent, MotionEvent>& event) {
56*38e8c45fSAndroid Build Coastguard Worker return std::visit([&](const auto& e) { return e.getId(); }, event);
57*38e8c45fSAndroid Build Coastguard Worker }
58*38e8c45fSAndroid Build Coastguard Worker
startTrace(const std::function<void (protozero::HeapBuffered<AndroidInputEventConfig> &)> & configure)59*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<perfetto::TracingSession> startTrace(
60*38e8c45fSAndroid Build Coastguard Worker const std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)>& configure) {
61*38e8c45fSAndroid Build Coastguard Worker protozero::HeapBuffered<AndroidInputEventConfig> inputEventConfig{};
62*38e8c45fSAndroid Build Coastguard Worker configure(inputEventConfig);
63*38e8c45fSAndroid Build Coastguard Worker
64*38e8c45fSAndroid Build Coastguard Worker perfetto::TraceConfig config;
65*38e8c45fSAndroid Build Coastguard Worker config.add_buffers()->set_size_kb(1024); // Record up to 1 MiB.
66*38e8c45fSAndroid Build Coastguard Worker auto* dataSourceConfig = config.add_data_sources()->mutable_config();
67*38e8c45fSAndroid Build Coastguard Worker dataSourceConfig->set_name("android.input.inputevent");
68*38e8c45fSAndroid Build Coastguard Worker dataSourceConfig->set_android_input_event_config_raw(inputEventConfig.SerializeAsString());
69*38e8c45fSAndroid Build Coastguard Worker
70*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<perfetto::TracingSession> tracingSession(perfetto::Tracing::NewTrace());
71*38e8c45fSAndroid Build Coastguard Worker tracingSession->Setup(config);
72*38e8c45fSAndroid Build Coastguard Worker tracingSession->StartBlocking();
73*38e8c45fSAndroid Build Coastguard Worker return tracingSession;
74*38e8c45fSAndroid Build Coastguard Worker }
75*38e8c45fSAndroid Build Coastguard Worker
stopTrace(std::unique_ptr<perfetto::TracingSession> tracingSession)76*38e8c45fSAndroid Build Coastguard Worker std::string stopTrace(std::unique_ptr<perfetto::TracingSession> tracingSession) {
77*38e8c45fSAndroid Build Coastguard Worker tracingSession->StopBlocking();
78*38e8c45fSAndroid Build Coastguard Worker std::vector<char> traceChars(tracingSession->ReadTraceBlocking());
79*38e8c45fSAndroid Build Coastguard Worker return {traceChars.data(), traceChars.size()};
80*38e8c45fSAndroid Build Coastguard Worker }
81*38e8c45fSAndroid Build Coastguard Worker
82*38e8c45fSAndroid Build Coastguard Worker // Decodes the trace, and returns all of the traced input events, and whether they were each
83*38e8c45fSAndroid Build Coastguard Worker // traced as a redacted event.
decodeTrace(const std::string & rawTrace)84*38e8c45fSAndroid Build Coastguard Worker auto decodeTrace(const std::string& rawTrace) {
85*38e8c45fSAndroid Build Coastguard Worker using namespace perfetto::protos::pbzero;
86*38e8c45fSAndroid Build Coastguard Worker
87*38e8c45fSAndroid Build Coastguard Worker ArrayMap<AndroidMotionEvent::Decoder, bool /*redacted*/> tracedMotions;
88*38e8c45fSAndroid Build Coastguard Worker ArrayMap<AndroidKeyEvent::Decoder, bool /*redacted*/> tracedKeys;
89*38e8c45fSAndroid Build Coastguard Worker ArrayMap<AndroidWindowInputDispatchEvent::Decoder, bool /*redacted*/> tracedWindowDispatches;
90*38e8c45fSAndroid Build Coastguard Worker
91*38e8c45fSAndroid Build Coastguard Worker Trace::Decoder trace{rawTrace};
92*38e8c45fSAndroid Build Coastguard Worker if (trace.has_packet()) {
93*38e8c45fSAndroid Build Coastguard Worker for (auto it = trace.packet(); it; it++) {
94*38e8c45fSAndroid Build Coastguard Worker TracePacket::Decoder packet{it->as_bytes()};
95*38e8c45fSAndroid Build Coastguard Worker if (!packet.has_winscope_extensions()) {
96*38e8c45fSAndroid Build Coastguard Worker continue;
97*38e8c45fSAndroid Build Coastguard Worker }
98*38e8c45fSAndroid Build Coastguard Worker
99*38e8c45fSAndroid Build Coastguard Worker WinscopeExtensions::Decoder extensions{packet.winscope_extensions()};
100*38e8c45fSAndroid Build Coastguard Worker const auto& field =
101*38e8c45fSAndroid Build Coastguard Worker extensions.Get(WinscopeExtensionsImpl::kAndroidInputEventFieldNumber);
102*38e8c45fSAndroid Build Coastguard Worker if (!field.valid()) {
103*38e8c45fSAndroid Build Coastguard Worker continue;
104*38e8c45fSAndroid Build Coastguard Worker }
105*38e8c45fSAndroid Build Coastguard Worker
106*38e8c45fSAndroid Build Coastguard Worker EXPECT_TRUE(packet.has_timestamp());
107*38e8c45fSAndroid Build Coastguard Worker EXPECT_TRUE(packet.has_timestamp_clock_id());
108*38e8c45fSAndroid Build Coastguard Worker EXPECT_EQ(packet.timestamp_clock_id(),
109*38e8c45fSAndroid Build Coastguard Worker static_cast<uint32_t>(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC));
110*38e8c45fSAndroid Build Coastguard Worker
111*38e8c45fSAndroid Build Coastguard Worker AndroidInputEvent::Decoder event{field.as_bytes()};
112*38e8c45fSAndroid Build Coastguard Worker if (event.has_dispatcher_motion_event()) {
113*38e8c45fSAndroid Build Coastguard Worker tracedMotions.emplace_back(event.dispatcher_motion_event(),
114*38e8c45fSAndroid Build Coastguard Worker /*redacted=*/false);
115*38e8c45fSAndroid Build Coastguard Worker }
116*38e8c45fSAndroid Build Coastguard Worker if (event.has_dispatcher_motion_event_redacted()) {
117*38e8c45fSAndroid Build Coastguard Worker tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(),
118*38e8c45fSAndroid Build Coastguard Worker /*redacted=*/true);
119*38e8c45fSAndroid Build Coastguard Worker }
120*38e8c45fSAndroid Build Coastguard Worker if (event.has_dispatcher_key_event()) {
121*38e8c45fSAndroid Build Coastguard Worker tracedKeys.emplace_back(event.dispatcher_key_event(),
122*38e8c45fSAndroid Build Coastguard Worker /*redacted=*/false);
123*38e8c45fSAndroid Build Coastguard Worker }
124*38e8c45fSAndroid Build Coastguard Worker if (event.has_dispatcher_key_event_redacted()) {
125*38e8c45fSAndroid Build Coastguard Worker tracedKeys.emplace_back(event.dispatcher_key_event_redacted(),
126*38e8c45fSAndroid Build Coastguard Worker /*redacted=*/true);
127*38e8c45fSAndroid Build Coastguard Worker }
128*38e8c45fSAndroid Build Coastguard Worker if (event.has_dispatcher_window_dispatch_event()) {
129*38e8c45fSAndroid Build Coastguard Worker tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(),
130*38e8c45fSAndroid Build Coastguard Worker /*redacted=*/false);
131*38e8c45fSAndroid Build Coastguard Worker }
132*38e8c45fSAndroid Build Coastguard Worker if (event.has_dispatcher_window_dispatch_event_redacted()) {
133*38e8c45fSAndroid Build Coastguard Worker tracedWindowDispatches
134*38e8c45fSAndroid Build Coastguard Worker .emplace_back(event.dispatcher_window_dispatch_event_redacted(),
135*38e8c45fSAndroid Build Coastguard Worker /*redacted=*/true);
136*38e8c45fSAndroid Build Coastguard Worker }
137*38e8c45fSAndroid Build Coastguard Worker }
138*38e8c45fSAndroid Build Coastguard Worker }
139*38e8c45fSAndroid Build Coastguard Worker return std::tuple{std::move(tracedMotions), std::move(tracedKeys),
140*38e8c45fSAndroid Build Coastguard Worker std::move(tracedWindowDispatches)};
141*38e8c45fSAndroid Build Coastguard Worker }
142*38e8c45fSAndroid Build Coastguard Worker
eventMatches(const MotionEvent & expected,const AndroidMotionEvent::Decoder & traced)143*38e8c45fSAndroid Build Coastguard Worker bool eventMatches(const MotionEvent& expected, const AndroidMotionEvent::Decoder& traced) {
144*38e8c45fSAndroid Build Coastguard Worker return static_cast<uint32_t>(expected.getId()) == traced.event_id();
145*38e8c45fSAndroid Build Coastguard Worker }
146*38e8c45fSAndroid Build Coastguard Worker
eventMatches(const KeyEvent & expected,const AndroidKeyEvent::Decoder & traced)147*38e8c45fSAndroid Build Coastguard Worker bool eventMatches(const KeyEvent& expected, const AndroidKeyEvent::Decoder& traced) {
148*38e8c45fSAndroid Build Coastguard Worker return static_cast<uint32_t>(expected.getId()) == traced.event_id();
149*38e8c45fSAndroid Build Coastguard Worker }
150*38e8c45fSAndroid Build Coastguard Worker
eventMatches(const InputTraceSession::WindowDispatchEvent & expected,const AndroidWindowInputDispatchEvent::Decoder & traced)151*38e8c45fSAndroid Build Coastguard Worker bool eventMatches(const InputTraceSession::WindowDispatchEvent& expected,
152*38e8c45fSAndroid Build Coastguard Worker const AndroidWindowInputDispatchEvent::Decoder& traced) {
153*38e8c45fSAndroid Build Coastguard Worker return static_cast<uint32_t>(getId(expected.event)) == traced.event_id() &&
154*38e8c45fSAndroid Build Coastguard Worker expected.window->getId() == traced.window_id();
155*38e8c45fSAndroid Build Coastguard Worker }
156*38e8c45fSAndroid Build Coastguard Worker
157*38e8c45fSAndroid Build Coastguard Worker template <typename ExpectedEvents, typename TracedEvents>
verifyExpectedEventsTraced(const ExpectedEvents & expectedEvents,const TracedEvents & tracedEvents,std::string_view name)158*38e8c45fSAndroid Build Coastguard Worker void verifyExpectedEventsTraced(const ExpectedEvents& expectedEvents,
159*38e8c45fSAndroid Build Coastguard Worker const TracedEvents& tracedEvents, std::string_view name) {
160*38e8c45fSAndroid Build Coastguard Worker uint32_t totalExpectedCount = 0;
161*38e8c45fSAndroid Build Coastguard Worker
162*38e8c45fSAndroid Build Coastguard Worker for (const auto& [expectedEvent, expectedLevel] : expectedEvents) {
163*38e8c45fSAndroid Build Coastguard Worker int32_t totalMatchCount = 0;
164*38e8c45fSAndroid Build Coastguard Worker int32_t redactedMatchCount = 0;
165*38e8c45fSAndroid Build Coastguard Worker for (const auto& [tracedEvent, isRedacted] : tracedEvents) {
166*38e8c45fSAndroid Build Coastguard Worker if (eventMatches(expectedEvent, tracedEvent)) {
167*38e8c45fSAndroid Build Coastguard Worker totalMatchCount++;
168*38e8c45fSAndroid Build Coastguard Worker if (isRedacted) {
169*38e8c45fSAndroid Build Coastguard Worker redactedMatchCount++;
170*38e8c45fSAndroid Build Coastguard Worker }
171*38e8c45fSAndroid Build Coastguard Worker }
172*38e8c45fSAndroid Build Coastguard Worker }
173*38e8c45fSAndroid Build Coastguard Worker switch (expectedLevel) {
174*38e8c45fSAndroid Build Coastguard Worker case Level::NONE:
175*38e8c45fSAndroid Build Coastguard Worker ASSERT_EQ(totalMatchCount, 0) << "Event should not be traced, but it was traced"
176*38e8c45fSAndroid Build Coastguard Worker << "\n\tExpected event: " << expectedEvent;
177*38e8c45fSAndroid Build Coastguard Worker break;
178*38e8c45fSAndroid Build Coastguard Worker case Level::REDACTED:
179*38e8c45fSAndroid Build Coastguard Worker case Level::COMPLETE:
180*38e8c45fSAndroid Build Coastguard Worker ASSERT_EQ(totalMatchCount, 1)
181*38e8c45fSAndroid Build Coastguard Worker << "Event should match exactly one traced event, but it matched: "
182*38e8c45fSAndroid Build Coastguard Worker << totalMatchCount << "\n\tExpected event: " << expectedEvent;
183*38e8c45fSAndroid Build Coastguard Worker ASSERT_EQ(redactedMatchCount, expectedLevel == Level::REDACTED ? 1 : 0);
184*38e8c45fSAndroid Build Coastguard Worker totalExpectedCount++;
185*38e8c45fSAndroid Build Coastguard Worker break;
186*38e8c45fSAndroid Build Coastguard Worker }
187*38e8c45fSAndroid Build Coastguard Worker }
188*38e8c45fSAndroid Build Coastguard Worker
189*38e8c45fSAndroid Build Coastguard Worker ASSERT_EQ(tracedEvents.size(), totalExpectedCount)
190*38e8c45fSAndroid Build Coastguard Worker << "The number of traced " << name
191*38e8c45fSAndroid Build Coastguard Worker << " events does not exactly match the number of expected events";
192*38e8c45fSAndroid Build Coastguard Worker }
193*38e8c45fSAndroid Build Coastguard Worker
194*38e8c45fSAndroid Build Coastguard Worker } // namespace
195*38e8c45fSAndroid Build Coastguard Worker
InputTraceSession(std::function<void (protozero::HeapBuffered<AndroidInputEventConfig> &)> configure)196*38e8c45fSAndroid Build Coastguard Worker InputTraceSession::InputTraceSession(
197*38e8c45fSAndroid Build Coastguard Worker std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)> configure)
198*38e8c45fSAndroid Build Coastguard Worker : mPerfettoSession(startTrace(std::move(configure))) {}
199*38e8c45fSAndroid Build Coastguard Worker
~InputTraceSession()200*38e8c45fSAndroid Build Coastguard Worker InputTraceSession::~InputTraceSession() {
201*38e8c45fSAndroid Build Coastguard Worker const auto rawTrace = stopTrace(std::move(mPerfettoSession));
202*38e8c45fSAndroid Build Coastguard Worker verifyExpectations(rawTrace);
203*38e8c45fSAndroid Build Coastguard Worker }
204*38e8c45fSAndroid Build Coastguard Worker
expectMotionTraced(Level level,const MotionEvent & event)205*38e8c45fSAndroid Build Coastguard Worker void InputTraceSession::expectMotionTraced(Level level, const MotionEvent& event) {
206*38e8c45fSAndroid Build Coastguard Worker mExpectedMotions.emplace_back(event, level);
207*38e8c45fSAndroid Build Coastguard Worker }
208*38e8c45fSAndroid Build Coastguard Worker
expectKeyTraced(Level level,const KeyEvent & event)209*38e8c45fSAndroid Build Coastguard Worker void InputTraceSession::expectKeyTraced(Level level, const KeyEvent& event) {
210*38e8c45fSAndroid Build Coastguard Worker mExpectedKeys.emplace_back(event, level);
211*38e8c45fSAndroid Build Coastguard Worker }
212*38e8c45fSAndroid Build Coastguard Worker
expectDispatchTraced(Level level,const WindowDispatchEvent & event)213*38e8c45fSAndroid Build Coastguard Worker void InputTraceSession::expectDispatchTraced(Level level, const WindowDispatchEvent& event) {
214*38e8c45fSAndroid Build Coastguard Worker mExpectedWindowDispatches.emplace_back(event, level);
215*38e8c45fSAndroid Build Coastguard Worker }
216*38e8c45fSAndroid Build Coastguard Worker
verifyExpectations(const std::string & rawTrace)217*38e8c45fSAndroid Build Coastguard Worker void InputTraceSession::verifyExpectations(const std::string& rawTrace) {
218*38e8c45fSAndroid Build Coastguard Worker auto [tracedMotions, tracedKeys, tracedWindowDispatches] = decodeTrace(rawTrace);
219*38e8c45fSAndroid Build Coastguard Worker
220*38e8c45fSAndroid Build Coastguard Worker verifyExpectedEventsTraced(mExpectedMotions, tracedMotions, "motion");
221*38e8c45fSAndroid Build Coastguard Worker verifyExpectedEventsTraced(mExpectedKeys, tracedKeys, "key");
222*38e8c45fSAndroid Build Coastguard Worker verifyExpectedEventsTraced(mExpectedWindowDispatches, tracedWindowDispatches,
223*38e8c45fSAndroid Build Coastguard Worker "window dispatch");
224*38e8c45fSAndroid Build Coastguard Worker }
225*38e8c45fSAndroid Build Coastguard Worker
226*38e8c45fSAndroid Build Coastguard Worker } // namespace android
227