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