blob: bbc6d988473e1886026d83cf7a55c0838f815da6 [file] [log] [blame]
Prabir Pradhan0762b1f2023-06-22 23:08:18 +00001/*
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//! Contains the InputVerifier, used to validate a stream of input events.
18
19use crate::ffi::RustPointerProperties;
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -070020use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass};
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000021use log::info;
22use std::collections::HashMap;
23use std::collections::HashSet;
24
Siarhei Vishniakou93992432023-10-09 15:47:48 -070025fn verify_event(
26 action: MotionAction,
27 pointer_properties: &[RustPointerProperties],
28 flags: &MotionFlags,
29) -> Result<(), String> {
30 let pointer_count = pointer_properties.len();
31 if pointer_count < 1 {
32 return Err(format!("Invalid {} event: no pointers", action));
33 }
34 match action {
35 MotionAction::Down
36 | MotionAction::HoverEnter
37 | MotionAction::HoverExit
38 | MotionAction::HoverMove
39 | MotionAction::Up => {
40 if pointer_count != 1 {
41 return Err(format!(
42 "Invalid {} event: there are {} pointers in the event",
43 action, pointer_count
44 ));
45 }
46 }
47
48 MotionAction::Cancel => {
49 if !flags.contains(MotionFlags::CANCELED) {
50 return Err(format!(
51 "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {:#?}",
52 flags
53 ));
54 }
55 }
56
57 MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => {
58 if action_index >= pointer_count {
59 return Err(format!("Got {}, but event has {} pointer(s)", action, pointer_count));
60 }
61 }
62
63 _ => {}
64 }
65 Ok(())
66}
67
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000068/// The InputVerifier is used to validate a stream of input events.
69pub struct InputVerifier {
70 name: String,
71 should_log: bool,
72 touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -070073 hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000074}
75
76impl InputVerifier {
77 /// Create a new InputVerifier.
78 pub fn new(name: &str, should_log: bool) -> Self {
79 logger::init(
80 logger::Config::default()
81 .with_tag_on_device("InputVerifier")
82 .with_min_level(log::Level::Trace),
83 );
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -070084 Self {
85 name: name.to_owned(),
86 should_log,
87 touching_pointer_ids_by_device: HashMap::new(),
88 hovering_pointer_ids_by_device: HashMap::new(),
89 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000090 }
91
92 /// Process a pointer movement event from an InputDevice.
93 /// If the event is not valid, we return an error string that describes the issue.
94 pub fn process_movement(
95 &mut self,
96 device_id: DeviceId,
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -070097 source: Source,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000098 action: u32,
99 pointer_properties: &[RustPointerProperties],
100 flags: MotionFlags,
101 ) -> Result<(), String> {
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700102 if !source.is_from_class(SourceClass::Pointer) {
103 // Skip non-pointer sources like MOUSE_RELATIVE for now
104 return Ok(());
105 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000106 if self.should_log {
107 info!(
108 "Processing {} for device {:?} ({} pointer{}) on {}",
109 MotionAction::from(action).to_string(),
110 device_id,
111 pointer_properties.len(),
112 if pointer_properties.len() == 1 { "" } else { "s" },
113 self.name
114 );
115 }
116
Siarhei Vishniakou93992432023-10-09 15:47:48 -0700117 verify_event(action.into(), pointer_properties, &flags)?;
118
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000119 match action.into() {
120 MotionAction::Down => {
121 let it = self
122 .touching_pointer_ids_by_device
123 .entry(device_id)
124 .or_insert_with(HashSet::new);
125 let pointer_id = pointer_properties[0].id;
126 if it.contains(&pointer_id) {
127 return Err(format!(
128 "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
129 self.name, device_id, it
130 ));
131 }
132 it.insert(pointer_id);
133 }
134 MotionAction::PointerDown { action_index } => {
135 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
136 return Err(format!(
137 "{}: Received POINTER_DOWN but no pointers are currently down \
138 for device {:?}",
139 self.name, device_id
140 ));
141 }
142 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700143 if it.len() != pointer_properties.len() - 1 {
144 return Err(format!(
145 "{}: There are currently {} touching pointers, but the incoming \
146 POINTER_DOWN event has {}",
147 self.name,
148 it.len(),
149 pointer_properties.len()
150 ));
151 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000152 let pointer_id = pointer_properties[action_index].id;
153 if it.contains(&pointer_id) {
154 return Err(format!(
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700155 "{}: Pointer with id={} already present found in the properties",
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000156 self.name, pointer_id
157 ));
158 }
159 it.insert(pointer_id);
160 }
161 MotionAction::Move => {
162 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
163 return Err(format!(
164 "{}: ACTION_MOVE touching pointers don't match",
165 self.name
166 ));
167 }
168 }
169 MotionAction::PointerUp { action_index } => {
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700170 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000171 return Err(format!(
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700172 "{}: ACTION_POINTER_UP touching pointers don't match",
173 self.name
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000174 ));
175 }
176 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
177 let pointer_id = pointer_properties[action_index].id;
178 it.remove(&pointer_id);
179 }
180 MotionAction::Up => {
181 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
182 return Err(format!(
183 "{} Received ACTION_UP but no pointers are currently down for device {:?}",
184 self.name, device_id
185 ));
186 }
187 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
188 if it.len() != 1 {
189 return Err(format!(
190 "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
191 self.name, it, device_id
192 ));
193 }
194 let pointer_id = pointer_properties[0].id;
195 if !it.contains(&pointer_id) {
196 return Err(format!(
197 "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
198 {:?} for device {:?}",
199 self.name, pointer_id, it, device_id
200 ));
201 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700202 self.touching_pointer_ids_by_device.remove(&device_id);
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000203 }
204 MotionAction::Cancel => {
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000205 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
206 return Err(format!(
207 "{}: Got ACTION_CANCEL, but the pointers don't match. \
208 Existing pointers: {:?}",
209 self.name, self.touching_pointer_ids_by_device
210 ));
211 }
212 self.touching_pointer_ids_by_device.remove(&device_id);
213 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700214 /*
215 * The hovering protocol currently supports a single pointer only, because we do not
216 * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
217 * Still, we are keeping the infrastructure here pretty general in case that is
218 * eventually supported.
219 */
220 MotionAction::HoverEnter => {
221 if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
222 return Err(format!(
223 "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
224 {:?}",
225 self.name, device_id, self.hovering_pointer_ids_by_device
226 ));
227 }
228 let it = self
229 .hovering_pointer_ids_by_device
230 .entry(device_id)
231 .or_insert_with(HashSet::new);
232 it.insert(pointer_properties[0].id);
233 }
234 MotionAction::HoverMove => {
235 // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
236 // If there was no prior HOVER_ENTER, just start a new hovering pointer.
237 let it = self
238 .hovering_pointer_ids_by_device
239 .entry(device_id)
240 .or_insert_with(HashSet::new);
241 it.insert(pointer_properties[0].id);
242 }
243 MotionAction::HoverExit => {
244 if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
245 return Err(format!(
246 "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
247 self.name, device_id
248 ));
249 }
250 let pointer_id = pointer_properties[0].id;
251 let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
252 it.remove(&pointer_id);
253
254 if !it.is_empty() {
255 return Err(format!(
256 "{}: Removed hovering pointer {}, but pointers are still\
257 hovering for device {:?}: {:?}",
258 self.name, pointer_id, device_id, it
259 ));
260 }
261 self.hovering_pointer_ids_by_device.remove(&device_id);
262 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000263 _ => return Ok(()),
264 }
265 Ok(())
266 }
267
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700268 /// Notify the verifier that the device has been reset, which will cause the verifier to erase
269 /// the current internal state for this device. Subsequent events from this device are expected
270 //// to start a new gesture.
271 pub fn reset_device(&mut self, device_id: DeviceId) {
272 self.touching_pointer_ids_by_device.remove(&device_id);
273 self.hovering_pointer_ids_by_device.remove(&device_id);
274 }
275
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000276 fn ensure_touching_pointers_match(
277 &self,
278 device_id: DeviceId,
279 pointer_properties: &[RustPointerProperties],
280 ) -> bool {
281 let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
282 return false;
283 };
284
285 for pointer_property in pointer_properties.iter() {
286 let pointer_id = pointer_property.id;
287 if !pointers.contains(&pointer_id) {
288 return false;
289 }
290 }
291 true
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use crate::input_verifier::InputVerifier;
298 use crate::DeviceId;
299 use crate::MotionFlags;
300 use crate::RustPointerProperties;
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700301 use crate::Source;
Siarhei Vishniakou93992432023-10-09 15:47:48 -0700302
303 #[test]
304 /**
305 * Send a DOWN event with 2 pointers and ensure that it's marked as invalid.
306 */
307 fn bad_down_event() {
308 let mut verifier = InputVerifier::new("Test", /*should_log*/ true);
309 let pointer_properties =
310 Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
311 assert!(verifier
312 .process_movement(
313 DeviceId(1),
314 Source::Touchscreen,
315 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
316 &pointer_properties,
317 MotionFlags::empty(),
318 )
319 .is_err());
320 }
321
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000322 #[test]
323 fn single_pointer_stream() {
324 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
325 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
326 assert!(verifier
327 .process_movement(
328 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700329 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000330 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
331 &pointer_properties,
332 MotionFlags::empty(),
333 )
334 .is_ok());
335 assert!(verifier
336 .process_movement(
337 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700338 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000339 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
340 &pointer_properties,
341 MotionFlags::empty(),
342 )
343 .is_ok());
344 assert!(verifier
345 .process_movement(
346 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700347 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000348 input_bindgen::AMOTION_EVENT_ACTION_UP,
349 &pointer_properties,
350 MotionFlags::empty(),
351 )
352 .is_ok());
353 }
354
355 #[test]
356 fn multi_device_stream() {
357 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
358 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
359 assert!(verifier
360 .process_movement(
361 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700362 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000363 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
364 &pointer_properties,
365 MotionFlags::empty(),
366 )
367 .is_ok());
368 assert!(verifier
369 .process_movement(
370 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700371 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000372 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
373 &pointer_properties,
374 MotionFlags::empty(),
375 )
376 .is_ok());
377 assert!(verifier
378 .process_movement(
379 DeviceId(2),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700380 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000381 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
382 &pointer_properties,
383 MotionFlags::empty(),
384 )
385 .is_ok());
386 assert!(verifier
387 .process_movement(
388 DeviceId(2),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700389 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000390 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
391 &pointer_properties,
392 MotionFlags::empty(),
393 )
394 .is_ok());
395 assert!(verifier
396 .process_movement(
397 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700398 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000399 input_bindgen::AMOTION_EVENT_ACTION_UP,
400 &pointer_properties,
401 MotionFlags::empty(),
402 )
403 .is_ok());
404 }
405
406 #[test]
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700407 fn action_cancel() {
408 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
409 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
410 assert!(verifier
411 .process_movement(
412 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700413 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700414 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
415 &pointer_properties,
416 MotionFlags::empty(),
417 )
418 .is_ok());
419 assert!(verifier
420 .process_movement(
421 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700422 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700423 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
424 &pointer_properties,
425 MotionFlags::CANCELED,
426 )
427 .is_ok());
428 }
429
430 #[test]
431 fn invalid_action_cancel() {
432 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
433 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
434 assert!(verifier
435 .process_movement(
436 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700437 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700438 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
439 &pointer_properties,
440 MotionFlags::empty(),
441 )
442 .is_ok());
443 assert!(verifier
444 .process_movement(
445 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700446 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700447 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
448 &pointer_properties,
449 MotionFlags::empty(), // forgot to set FLAG_CANCELED
450 )
451 .is_err());
452 }
453
454 #[test]
455 fn invalid_up() {
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000456 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
457 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
458 assert!(verifier
459 .process_movement(
460 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700461 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000462 input_bindgen::AMOTION_EVENT_ACTION_UP,
463 &pointer_properties,
464 MotionFlags::empty(),
465 )
466 .is_err());
467 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700468
469 #[test]
470 fn correct_hover_sequence() {
471 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
472 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
473 assert!(verifier
474 .process_movement(
475 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700476 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700477 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
478 &pointer_properties,
479 MotionFlags::empty(),
480 )
481 .is_ok());
482
483 assert!(verifier
484 .process_movement(
485 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700486 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700487 input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
488 &pointer_properties,
489 MotionFlags::empty(),
490 )
491 .is_ok());
492
493 assert!(verifier
494 .process_movement(
495 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700496 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700497 input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
498 &pointer_properties,
499 MotionFlags::empty(),
500 )
501 .is_ok());
502
503 assert!(verifier
504 .process_movement(
505 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700506 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700507 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
508 &pointer_properties,
509 MotionFlags::empty(),
510 )
511 .is_ok());
512 }
513
514 #[test]
515 fn double_hover_enter() {
516 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
517 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
518 assert!(verifier
519 .process_movement(
520 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700521 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700522 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
523 &pointer_properties,
524 MotionFlags::empty(),
525 )
526 .is_ok());
527
528 assert!(verifier
529 .process_movement(
530 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700531 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700532 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
533 &pointer_properties,
534 MotionFlags::empty(),
535 )
536 .is_err());
537 }
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700538
539 // Send a MOVE without a preceding DOWN event. This is OK because it's from source
540 // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event.
541 #[test]
542 fn relative_mouse_move() {
543 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
544 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
545 assert!(verifier
546 .process_movement(
547 DeviceId(2),
548 Source::MouseRelative,
549 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
550 &pointer_properties,
551 MotionFlags::empty(),
552 )
553 .is_ok());
554 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000555}