blob: 32acb5f2886c3c73ac9afa715a7b2b06d85118fe [file] [log] [blame]
Prabir Pradhan9a9897d2024-03-21 21:52:57 +00001/*
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#include "InputTraceSession.h"
18
19#include <NotifyArgsBuilders.h>
20#include <android-base/logging.h>
21#include <gtest/gtest.h>
22#include <input/PrintTools.h>
23
24#include <utility>
25
26namespace android {
27
28using perfetto::protos::pbzero::AndroidInputEvent;
29using perfetto::protos::pbzero::AndroidInputEventConfig;
30using perfetto::protos::pbzero::AndroidKeyEvent;
31using perfetto::protos::pbzero::AndroidMotionEvent;
32using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent;
33
34// These operator<< definitions must be in the global namespace for them to be accessible to the
35// GTEST library. They cannot be in the anonymous namespace.
36static std::ostream& operator<<(std::ostream& out,
37 const std::variant<KeyEvent, MotionEvent>& event) {
38 std::visit([&](const auto& e) { out << e; }, event);
39 return out;
40}
41
42static std::ostream& operator<<(std::ostream& out,
43 const InputTraceSession::WindowDispatchEvent& event) {
44 out << "Window dispatch to windowId: " << event.window->getId() << ", event: " << event.event;
45 return out;
46}
47
48namespace {
49
50inline uint32_t getId(const std::variant<KeyEvent, MotionEvent>& event) {
51 return std::visit([&](const auto& e) { return e.getId(); }, event);
52}
53
54std::unique_ptr<perfetto::TracingSession> startTrace(
55 const std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)>& configure) {
56 protozero::HeapBuffered<AndroidInputEventConfig> inputEventConfig{};
57 configure(inputEventConfig);
58
59 perfetto::TraceConfig config;
60 config.add_buffers()->set_size_kb(1024); // Record up to 1 MiB.
61 auto* dataSourceConfig = config.add_data_sources()->mutable_config();
62 dataSourceConfig->set_name("android.input.inputevent");
63 dataSourceConfig->set_android_input_event_config_raw(inputEventConfig.SerializeAsString());
64
65 std::unique_ptr<perfetto::TracingSession> tracingSession(perfetto::Tracing::NewTrace());
66 tracingSession->Setup(config);
67 tracingSession->StartBlocking();
68 return tracingSession;
69}
70
71std::string stopTrace(std::unique_ptr<perfetto::TracingSession> tracingSession) {
72 tracingSession->StopBlocking();
73 std::vector<char> traceChars(tracingSession->ReadTraceBlocking());
74 return {traceChars.data(), traceChars.size()};
75}
76
77// Decodes the trace, and returns all of the traced input events, and whether they were each
78// traced as a redacted event.
79auto decodeTrace(const std::string& rawTrace) {
80 using namespace perfetto::protos::pbzero;
81
82 ArrayMap<AndroidMotionEvent::Decoder, bool /*redacted*/> tracedMotions;
83 ArrayMap<AndroidKeyEvent::Decoder, bool /*redacted*/> tracedKeys;
84 ArrayMap<AndroidWindowInputDispatchEvent::Decoder, bool /*redacted*/> tracedWindowDispatches;
85
86 Trace::Decoder trace{rawTrace};
87 if (trace.has_packet()) {
Priyanka Advani8779d082024-06-10 19:07:02 +000088 auto it = trace.packet();
89 while (it) {
Prabir Pradhan9a9897d2024-03-21 21:52:57 +000090 TracePacket::Decoder packet{it->as_bytes()};
Priyanka Advani8779d082024-06-10 19:07:02 +000091 if (packet.has_android_input_event()) {
92 AndroidInputEvent::Decoder event{packet.android_input_event()};
93 if (event.has_dispatcher_motion_event()) {
94 tracedMotions.emplace_back(event.dispatcher_motion_event(),
95 /*redacted=*/false);
96 }
97 if (event.has_dispatcher_motion_event_redacted()) {
98 tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(),
99 /*redacted=*/true);
100 }
101 if (event.has_dispatcher_key_event()) {
102 tracedKeys.emplace_back(event.dispatcher_key_event(),
103 /*redacted=*/false);
104 }
105 if (event.has_dispatcher_key_event_redacted()) {
106 tracedKeys.emplace_back(event.dispatcher_key_event_redacted(),
107 /*redacted=*/true);
108 }
109 if (event.has_dispatcher_window_dispatch_event()) {
110 tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(),
111 /*redacted=*/false);
112 }
113 if (event.has_dispatcher_window_dispatch_event_redacted()) {
114 tracedWindowDispatches
115 .emplace_back(event.dispatcher_window_dispatch_event_redacted(),
116 /*redacted=*/true);
117 }
Prabir Pradhan9a9897d2024-03-21 21:52:57 +0000118 }
Priyanka Advani8779d082024-06-10 19:07:02 +0000119 it++;
Prabir Pradhan9a9897d2024-03-21 21:52:57 +0000120 }
121 }
122 return std::tuple{std::move(tracedMotions), std::move(tracedKeys),
123 std::move(tracedWindowDispatches)};
124}
125
126bool eventMatches(const MotionEvent& expected, const AndroidMotionEvent::Decoder& traced) {
127 return static_cast<uint32_t>(expected.getId()) == traced.event_id();
128}
129
130bool eventMatches(const KeyEvent& expected, const AndroidKeyEvent::Decoder& traced) {
131 return static_cast<uint32_t>(expected.getId()) == traced.event_id();
132}
133
134bool eventMatches(const InputTraceSession::WindowDispatchEvent& expected,
135 const AndroidWindowInputDispatchEvent::Decoder& traced) {
136 return static_cast<uint32_t>(getId(expected.event)) == traced.event_id() &&
137 expected.window->getId() == traced.window_id();
138}
139
140template <typename ExpectedEvents, typename TracedEvents>
141void verifyExpectedEventsTraced(const ExpectedEvents& expectedEvents,
142 const TracedEvents& tracedEvents, std::string_view name) {
143 uint32_t totalExpectedCount = 0;
144
145 for (const auto& [expectedEvent, expectedLevel] : expectedEvents) {
146 int32_t totalMatchCount = 0;
147 int32_t redactedMatchCount = 0;
148 for (const auto& [tracedEvent, isRedacted] : tracedEvents) {
149 if (eventMatches(expectedEvent, tracedEvent)) {
150 totalMatchCount++;
151 if (isRedacted) {
152 redactedMatchCount++;
153 }
154 }
155 }
156 switch (expectedLevel) {
157 case Level::NONE:
158 ASSERT_EQ(totalMatchCount, 0) << "Event should not be traced, but it was traced"
159 << "\n\tExpected event: " << expectedEvent;
160 break;
161 case Level::REDACTED:
162 case Level::COMPLETE:
163 ASSERT_EQ(totalMatchCount, 1)
164 << "Event should match exactly one traced event, but it matched: "
165 << totalMatchCount << "\n\tExpected event: " << expectedEvent;
166 ASSERT_EQ(redactedMatchCount, expectedLevel == Level::REDACTED ? 1 : 0);
167 totalExpectedCount++;
168 break;
169 }
170 }
171
172 ASSERT_EQ(tracedEvents.size(), totalExpectedCount)
173 << "The number of traced " << name
174 << " events does not exactly match the number of expected events";
175}
176
177} // namespace
178
179InputTraceSession::InputTraceSession(
180 std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)> configure)
181 : mPerfettoSession(startTrace(std::move(configure))) {}
182
183InputTraceSession::~InputTraceSession() {
184 const auto rawTrace = stopTrace(std::move(mPerfettoSession));
185 verifyExpectations(rawTrace);
186}
187
188void InputTraceSession::expectMotionTraced(Level level, const MotionEvent& event) {
189 mExpectedMotions.emplace_back(event, level);
190}
191
192void InputTraceSession::expectKeyTraced(Level level, const KeyEvent& event) {
193 mExpectedKeys.emplace_back(event, level);
194}
195
196void InputTraceSession::expectDispatchTraced(Level level, const WindowDispatchEvent& event) {
197 mExpectedWindowDispatches.emplace_back(event, level);
198}
199
200void InputTraceSession::verifyExpectations(const std::string& rawTrace) {
201 auto [tracedMotions, tracedKeys, tracedWindowDispatches] = decodeTrace(rawTrace);
202
203 verifyExpectedEventsTraced(mExpectedMotions, tracedMotions, "motion");
204 verifyExpectedEventsTraced(mExpectedKeys, tracedKeys, "key");
205 verifyExpectedEventsTraced(mExpectedWindowDispatches, tracedWindowDispatches,
206 "window dispatch");
207}
208
209} // namespace android