• 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 #pragma once
18 
19 #include <perfetto/config/android/android_input_event_config.pbzero.h>
20 #include <perfetto/trace/android/android_input_event.pbzero.h>
21 
22 #include "InputTracingBackendInterface.h"
23 #include "InputTracingPerfettoBackendConfig.h"
24 
25 namespace proto = perfetto::protos::pbzero;
26 
27 namespace android::inputdispatcher::trace {
28 
29 namespace internal {
30 
31 using namespace ftl::flag_operators;
32 
33 // The trace config to use for maximal tracing.
34 const impl::TraceConfig CONFIG_TRACE_ALL{
35         .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS |
36                 impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH,
37         .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE,
38                                   .matchAllPackages = {},
39                                   .matchAnyPackages = {},
40                                   .matchSecure{},
41                                   .matchImeConnectionActive = {}}},
42 };
43 
44 template <typename Pointer>
writeAxisValue(Pointer * pointer,int32_t axis,float value,bool isRedacted)45 void writeAxisValue(Pointer* pointer, int32_t axis, float value, bool isRedacted) {
46     auto* axisEntry = pointer->add_axis_value();
47     axisEntry->set_axis(axis);
48 
49     if (!isRedacted) {
50         axisEntry->set_value(value);
51     }
52 }
53 
54 } // namespace internal
55 
56 /**
57  * Write traced events into Perfetto protos.
58  *
59  * This class is templated so that the logic can be tested while substituting the proto classes
60  * auto-generated by Perfetto's pbzero library with mock implementations.
61  */
62 template <typename ProtoMotion, typename ProtoKey, typename ProtoDispatch,
63           typename ProtoConfigDecoder>
64 class AndroidInputEventProtoConverter {
65 public:
toProtoMotionEvent(const TracedMotionEvent & event,ProtoMotion & outProto,bool isRedacted)66     static void toProtoMotionEvent(const TracedMotionEvent& event, ProtoMotion& outProto,
67                                    bool isRedacted) {
68         outProto.set_event_id(event.id);
69         outProto.set_event_time_nanos(event.eventTime);
70         outProto.set_down_time_nanos(event.downTime);
71         outProto.set_source(event.source);
72         outProto.set_action(event.action);
73         outProto.set_device_id(event.deviceId);
74         outProto.set_display_id(event.displayId.val());
75         outProto.set_classification(static_cast<int32_t>(event.classification));
76         outProto.set_flags(event.flags);
77         outProto.set_policy_flags(event.policyFlags);
78         outProto.set_button_state(event.buttonState);
79         outProto.set_action_button(event.actionButton);
80 
81         if (!isRedacted) {
82             outProto.set_cursor_position_x(event.xCursorPosition);
83             outProto.set_cursor_position_y(event.yCursorPosition);
84             outProto.set_meta_state(event.metaState);
85             outProto.set_precision_x(event.xPrecision);
86             outProto.set_precision_y(event.yPrecision);
87         }
88 
89         for (uint32_t i = 0; i < event.pointerProperties.size(); i++) {
90             auto* pointer = outProto.add_pointer();
91 
92             const auto& props = event.pointerProperties[i];
93             pointer->set_pointer_id(props.id);
94             pointer->set_tool_type(static_cast<int32_t>(props.toolType));
95 
96             const auto& coords = event.pointerCoords[i];
97             auto bits = BitSet64(coords.bits);
98 
99             if (isFromSource(event.source, AINPUT_SOURCE_CLASS_POINTER)) {
100                 // Always include the X and Y axes for pointer events, since the
101                 // bits will not be marked if the value is 0.
102                 for (const auto axis : {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y}) {
103                     if (!bits.hasBit(axis)) {
104                         internal::writeAxisValue(pointer, axis, 0.0f, isRedacted);
105                     }
106                 }
107             }
108 
109             for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
110                 const auto axis = bits.clearFirstMarkedBit();
111                 internal::writeAxisValue(pointer, axis, coords.values[axisIndex], isRedacted);
112             }
113         }
114     }
115 
toProtoKeyEvent(const TracedKeyEvent & event,ProtoKey & outProto,bool isRedacted)116     static void toProtoKeyEvent(const TracedKeyEvent& event, ProtoKey& outProto, bool isRedacted) {
117         outProto.set_event_id(event.id);
118         outProto.set_event_time_nanos(event.eventTime);
119         outProto.set_down_time_nanos(event.downTime);
120         outProto.set_source(event.source);
121         outProto.set_action(event.action);
122         outProto.set_device_id(event.deviceId);
123         outProto.set_display_id(event.displayId.val());
124         outProto.set_repeat_count(event.repeatCount);
125         outProto.set_flags(event.flags);
126         outProto.set_policy_flags(event.policyFlags);
127 
128         if (!isRedacted) {
129             outProto.set_key_code(event.keyCode);
130             outProto.set_scan_code(event.scanCode);
131             outProto.set_meta_state(event.metaState);
132         }
133     }
134 
toProtoWindowDispatchEvent(const WindowDispatchArgs & args,ProtoDispatch & outProto,bool isRedacted)135     static void toProtoWindowDispatchEvent(const WindowDispatchArgs& args, ProtoDispatch& outProto,
136                                            bool isRedacted) {
137         std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry);
138         outProto.set_vsync_id(args.vsyncId);
139         outProto.set_window_id(args.windowId);
140         outProto.set_resolved_flags(args.resolvedFlags);
141 
142         if (isRedacted) {
143             return;
144         }
145         if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) {
146             for (size_t i = 0; i < motion->pointerProperties.size(); i++) {
147                 auto* pointerProto = outProto.add_dispatched_pointer();
148                 pointerProto->set_pointer_id(motion->pointerProperties[i].id);
149                 const auto& coords = motion->pointerCoords[i];
150                 const auto rawXY =
151                         MotionEvent::calculateTransformedXY(motion->source, args.rawTransform,
152                                                             coords.getXYValue());
153                 if (coords.getXYValue() != rawXY) {
154                     // These values are only traced if they were modified by the raw transform
155                     // to save space. Trace consumers should be aware of this optimization.
156                     pointerProto->set_x_in_display(rawXY.x);
157                     pointerProto->set_y_in_display(rawXY.y);
158                 }
159 
160                 const auto coordsInWindow =
161                         MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
162                                                                 args.transform, coords);
163                 auto bits = BitSet64(coords.bits);
164                 for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
165                     const uint32_t axis = bits.clearFirstMarkedBit();
166                     const float axisValueInWindow = coordsInWindow.values[axisIndex];
167                     // Only values that are modified by the window transform are traced.
168                     if (coords.values[axisIndex] != axisValueInWindow) {
169                         auto* axisEntry = pointerProto->add_axis_value_in_window();
170                         axisEntry->set_axis(axis);
171                         axisEntry->set_value(axisValueInWindow);
172                     }
173                 }
174             }
175         }
176     }
177 
parseConfig(ProtoConfigDecoder & protoConfig)178     static impl::TraceConfig parseConfig(ProtoConfigDecoder& protoConfig) {
179         if (protoConfig.has_mode() &&
180             protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) {
181             // User has requested the preset for maximal tracing
182             return internal::CONFIG_TRACE_ALL;
183         }
184 
185         impl::TraceConfig config;
186 
187         // Parse trace flags
188         if (protoConfig.has_trace_dispatcher_input_events() &&
189             protoConfig.trace_dispatcher_input_events()) {
190             config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS;
191         }
192         if (protoConfig.has_trace_dispatcher_window_dispatch() &&
193             protoConfig.trace_dispatcher_window_dispatch()) {
194             config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH;
195         }
196 
197         // Parse trace rules
198         auto rulesIt = protoConfig.rules();
199         while (rulesIt) {
200             proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()};
201             config.rules.emplace_back();
202             auto& rule = config.rules.back();
203 
204             rule.level = protoRule.has_trace_level()
205                     ? static_cast<impl::TraceLevel>(protoRule.trace_level())
206                     : impl::TraceLevel::TRACE_LEVEL_NONE;
207 
208             if (protoRule.has_match_all_packages()) {
209                 auto pkgIt = protoRule.match_all_packages();
210                 while (pkgIt) {
211                     rule.matchAllPackages.emplace_back(pkgIt->as_std_string());
212                     pkgIt++;
213                 }
214             }
215 
216             if (protoRule.has_match_any_packages()) {
217                 auto pkgIt = protoRule.match_any_packages();
218                 while (pkgIt) {
219                     rule.matchAnyPackages.emplace_back(pkgIt->as_std_string());
220                     pkgIt++;
221                 }
222             }
223 
224             if (protoRule.has_match_secure()) {
225                 rule.matchSecure = protoRule.match_secure();
226             }
227 
228             if (protoRule.has_match_ime_connection_active()) {
229                 rule.matchImeConnectionActive = protoRule.match_ime_connection_active();
230             }
231 
232             rulesIt++;
233         }
234 
235         return config;
236     }
237 };
238 
239 } // namespace android::inputdispatcher::trace
240