1 /*
2 * Copyright 2023 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 //! Validate the incoming motion stream.
18 //! This class is not thread-safe.
19 //! State is stored in the "InputVerifier" object
20 //! that can be created via the 'create' method.
21 //! Usage:
22 //! Box<InputVerifier> verifier = create("inputChannel name");
23 //! result = process_movement(verifier, ...);
24 //! if (result) {
25 //! crash(result.error_message());
26 //! }
27
28 use std::collections::HashMap;
29 use std::collections::HashSet;
30
31 use bitflags::bitflags;
32 use log::info;
33
34 #[cxx::bridge(namespace = "android::input")]
35 #[allow(unsafe_op_in_unsafe_fn)]
36 mod ffi {
37 #[namespace = "android"]
38 unsafe extern "C++" {
39 include!("ffi/FromRustToCpp.h");
shouldLog(tag: &str) -> bool40 fn shouldLog(tag: &str) -> bool;
41 }
42 #[namespace = "android::input::verifier"]
43 extern "Rust" {
44 type InputVerifier;
45
create(name: String) -> Box<InputVerifier>46 fn create(name: String) -> Box<InputVerifier>;
process_movement( verifier: &mut InputVerifier, device_id: i32, action: u32, pointer_properties: &[RustPointerProperties], flags: i32, ) -> String47 fn process_movement(
48 verifier: &mut InputVerifier,
49 device_id: i32,
50 action: u32,
51 pointer_properties: &[RustPointerProperties],
52 flags: i32,
53 ) -> String;
54 }
55
56 pub struct RustPointerProperties {
57 id: i32,
58 }
59 }
60
61 use crate::ffi::shouldLog;
62 use crate::ffi::RustPointerProperties;
63
64 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
65 struct DeviceId(i32);
66
process_movement( verifier: &mut InputVerifier, device_id: i32, action: u32, pointer_properties: &[RustPointerProperties], flags: i32, ) -> String67 fn process_movement(
68 verifier: &mut InputVerifier,
69 device_id: i32,
70 action: u32,
71 pointer_properties: &[RustPointerProperties],
72 flags: i32,
73 ) -> String {
74 let result = verifier.process_movement(
75 DeviceId(device_id),
76 action,
77 pointer_properties,
78 Flags::from_bits(flags).unwrap(),
79 );
80 match result {
81 Ok(()) => "".to_string(),
82 Err(e) => e,
83 }
84 }
85
create(name: String) -> Box<InputVerifier>86 fn create(name: String) -> Box<InputVerifier> {
87 Box::new(InputVerifier::new(&name))
88 }
89
90 #[repr(u32)]
91 enum MotionAction {
92 Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
93 Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
94 Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
95 Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
96 Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
97 PointerDown { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
98 PointerUp { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
99 HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
100 HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
101 HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
102 Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
103 ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
104 ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
105 }
106
get_action_index(action: u32) -> usize107 fn get_action_index(action: u32) -> usize {
108 let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
109 >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
110 index.try_into().unwrap()
111 }
112
113 impl From<u32> for MotionAction {
from(action: u32) -> Self114 fn from(action: u32) -> Self {
115 let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
116 let action_index = get_action_index(action);
117 match action_masked {
118 input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
119 input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
120 input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
121 input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
122 input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
123 input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
124 MotionAction::PointerDown { action_index }
125 }
126 input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
127 MotionAction::PointerUp { action_index }
128 }
129 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
130 input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
131 input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
132 input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
133 input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
134 input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
135 _ => panic!("Unknown action: {}", action),
136 }
137 }
138 }
139
140 bitflags! {
141 struct Flags: i32 {
142 const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
143 }
144 }
145
motion_action_to_string(action: u32) -> String146 fn motion_action_to_string(action: u32) -> String {
147 match action.into() {
148 MotionAction::Down => "DOWN".to_string(),
149 MotionAction::Up => "UP".to_string(),
150 MotionAction::Move => "MOVE".to_string(),
151 MotionAction::Cancel => "CANCEL".to_string(),
152 MotionAction::Outside => "OUTSIDE".to_string(),
153 MotionAction::PointerDown { action_index } => {
154 format!("POINTER_DOWN({})", action_index)
155 }
156 MotionAction::PointerUp { action_index } => {
157 format!("POINTER_UP({})", action_index)
158 }
159 MotionAction::HoverMove => "HOVER_MOVE".to_string(),
160 MotionAction::Scroll => "SCROLL".to_string(),
161 MotionAction::HoverEnter => "HOVER_ENTER".to_string(),
162 MotionAction::HoverExit => "HOVER_EXIT".to_string(),
163 MotionAction::ButtonPress => "BUTTON_PRESS".to_string(),
164 MotionAction::ButtonRelease => "BUTTON_RELEASE".to_string(),
165 }
166 }
167
168 /**
169 * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
170 * to inconsistent events.
171 * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
172 */
log_events() -> bool173 fn log_events() -> bool {
174 shouldLog("InputVerifierLogEvents")
175 }
176
177 struct InputVerifier {
178 name: String,
179 touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
180 }
181
182 impl InputVerifier {
new(name: &str) -> Self183 fn new(name: &str) -> Self {
184 logger::init(
185 logger::Config::default()
186 .with_tag_on_device("InputVerifier")
187 .with_min_level(log::Level::Trace),
188 );
189 Self { name: name.to_owned(), touching_pointer_ids_by_device: HashMap::new() }
190 }
191
process_movement( &mut self, device_id: DeviceId, action: u32, pointer_properties: &[RustPointerProperties], flags: Flags, ) -> Result<(), String>192 fn process_movement(
193 &mut self,
194 device_id: DeviceId,
195 action: u32,
196 pointer_properties: &[RustPointerProperties],
197 flags: Flags,
198 ) -> Result<(), String> {
199 if log_events() {
200 info!(
201 "Processing {} for device {:?} ({} pointer{}) on {}",
202 motion_action_to_string(action),
203 device_id,
204 pointer_properties.len(),
205 if pointer_properties.len() == 1 { "" } else { "s" },
206 self.name
207 );
208 }
209
210 match action.into() {
211 MotionAction::Down => {
212 let it = self
213 .touching_pointer_ids_by_device
214 .entry(device_id)
215 .or_insert_with(HashSet::new);
216 let pointer_id = pointer_properties[0].id;
217 if it.contains(&pointer_id) {
218 return Err(format!(
219 "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
220 self.name, device_id, it
221 ));
222 }
223 it.insert(pointer_id);
224 }
225 MotionAction::PointerDown { action_index } => {
226 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
227 return Err(format!(
228 "{}: Received POINTER_DOWN but no pointers are currently down \
229 for device {:?}",
230 self.name, device_id
231 ));
232 }
233 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
234 let pointer_id = pointer_properties[action_index].id;
235 if it.contains(&pointer_id) {
236 return Err(format!(
237 "{}: Pointer with id={} not found in the properties",
238 self.name, pointer_id
239 ));
240 }
241 it.insert(pointer_id);
242 }
243 MotionAction::Move => {
244 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
245 return Err(format!(
246 "{}: ACTION_MOVE touching pointers don't match",
247 self.name
248 ));
249 }
250 }
251 MotionAction::PointerUp { action_index } => {
252 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
253 return Err(format!(
254 "{}: Received POINTER_UP but no pointers are currently down for device \
255 {:?}",
256 self.name, device_id
257 ));
258 }
259 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
260 let pointer_id = pointer_properties[action_index].id;
261 it.remove(&pointer_id);
262 }
263 MotionAction::Up => {
264 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
265 return Err(format!(
266 "{} Received ACTION_UP but no pointers are currently down for device {:?}",
267 self.name, device_id
268 ));
269 }
270 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
271 if it.len() != 1 {
272 return Err(format!(
273 "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
274 self.name, it, device_id
275 ));
276 }
277 let pointer_id = pointer_properties[0].id;
278 if !it.contains(&pointer_id) {
279 return Err(format!(
280 "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
281 {:?} for device {:?}",
282 self.name, pointer_id, it, device_id
283 ));
284 }
285 it.clear();
286 }
287 MotionAction::Cancel => {
288 if flags.contains(Flags::CANCELED) {
289 return Err(format!(
290 "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
291 self.name
292 ));
293 }
294 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
295 return Err(format!(
296 "{}: Got ACTION_CANCEL, but the pointers don't match. \
297 Existing pointers: {:?}",
298 self.name, self.touching_pointer_ids_by_device
299 ));
300 }
301 self.touching_pointer_ids_by_device.remove(&device_id);
302 }
303 _ => return Ok(()),
304 }
305 Ok(())
306 }
307
ensure_touching_pointers_match( &self, device_id: DeviceId, pointer_properties: &[RustPointerProperties], ) -> bool308 fn ensure_touching_pointers_match(
309 &self,
310 device_id: DeviceId,
311 pointer_properties: &[RustPointerProperties],
312 ) -> bool {
313 let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
314 return false;
315 };
316
317 for pointer_property in pointer_properties.iter() {
318 let pointer_id = pointer_property.id;
319 if !pointers.contains(&pointer_id) {
320 return false;
321 }
322 }
323 true
324 }
325 }
326
327 #[cfg(test)]
328 mod tests {
329 use crate::DeviceId;
330 use crate::Flags;
331 use crate::InputVerifier;
332 use crate::RustPointerProperties;
333 #[test]
single_pointer_stream()334 fn single_pointer_stream() {
335 let mut verifier = InputVerifier::new("Test");
336 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
337 assert!(verifier
338 .process_movement(
339 DeviceId(1),
340 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
341 &pointer_properties,
342 Flags::empty(),
343 )
344 .is_ok());
345 assert!(verifier
346 .process_movement(
347 DeviceId(1),
348 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
349 &pointer_properties,
350 Flags::empty(),
351 )
352 .is_ok());
353 assert!(verifier
354 .process_movement(
355 DeviceId(1),
356 input_bindgen::AMOTION_EVENT_ACTION_UP,
357 &pointer_properties,
358 Flags::empty(),
359 )
360 .is_ok());
361 }
362
363 #[test]
multi_device_stream()364 fn multi_device_stream() {
365 let mut verifier = InputVerifier::new("Test");
366 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
367 assert!(verifier
368 .process_movement(
369 DeviceId(1),
370 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
371 &pointer_properties,
372 Flags::empty(),
373 )
374 .is_ok());
375 assert!(verifier
376 .process_movement(
377 DeviceId(1),
378 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
379 &pointer_properties,
380 Flags::empty(),
381 )
382 .is_ok());
383 assert!(verifier
384 .process_movement(
385 DeviceId(2),
386 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
387 &pointer_properties,
388 Flags::empty(),
389 )
390 .is_ok());
391 assert!(verifier
392 .process_movement(
393 DeviceId(2),
394 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
395 &pointer_properties,
396 Flags::empty(),
397 )
398 .is_ok());
399 assert!(verifier
400 .process_movement(
401 DeviceId(1),
402 input_bindgen::AMOTION_EVENT_ACTION_UP,
403 &pointer_properties,
404 Flags::empty(),
405 )
406 .is_ok());
407 }
408
409 #[test]
test_invalid_up()410 fn test_invalid_up() {
411 let mut verifier = InputVerifier::new("Test");
412 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
413 assert!(verifier
414 .process_movement(
415 DeviceId(1),
416 input_bindgen::AMOTION_EVENT_ACTION_UP,
417 &pointer_properties,
418 Flags::empty(),
419 )
420 .is_err());
421 }
422 }
423