xref: /aosp_15_r20/frameworks/native/libs/input/rust/input_verifier.rs (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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 //! Contains the InputVerifier, used to validate a stream of input events.
18 
19 use crate::ffi::RustPointerProperties;
20 use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass};
21 use log::info;
22 use std::collections::HashMap;
23 use std::collections::HashSet;
24 
verify_event( action: MotionAction, pointer_properties: &[RustPointerProperties], flags: &MotionFlags, ) -> Result<(), String>25 fn 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 
68 /// The InputVerifier is used to validate a stream of input events.
69 pub struct InputVerifier {
70     name: String,
71     should_log: bool,
72     touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
73     hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
74 }
75 
76 impl InputVerifier {
77     /// Create a new InputVerifier.
new(name: &str, should_log: bool) -> Self78     pub fn new(name: &str, should_log: bool) -> Self {
79         logger::init(
80             logger::Config::default()
81                 .with_tag_on_device("InputVerifier")
82                 .with_max_level(log::LevelFilter::Trace),
83         );
84         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         }
90     }
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.
process_movement( &mut self, device_id: DeviceId, source: Source, action: u32, pointer_properties: &[RustPointerProperties], flags: MotionFlags, ) -> Result<(), String>94     pub fn process_movement(
95         &mut self,
96         device_id: DeviceId,
97         source: Source,
98         action: u32,
99         pointer_properties: &[RustPointerProperties],
100         flags: MotionFlags,
101     ) -> Result<(), String> {
102         if !source.is_from_class(SourceClass::Pointer) {
103             // Skip non-pointer sources like MOUSE_RELATIVE for now
104             return Ok(());
105         }
106         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 
117         verify_event(action.into(), pointer_properties, &flags)?;
118 
119         match action.into() {
120             MotionAction::Down => {
121                 if self.touching_pointer_ids_by_device.contains_key(&device_id) {
122                     return Err(format!(
123                         "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
124                         self.name, device_id, self.touching_pointer_ids_by_device
125                     ));
126                 }
127                 let it = self.touching_pointer_ids_by_device.entry(device_id).or_default();
128                 it.insert(pointer_properties[0].id);
129             }
130             MotionAction::PointerDown { action_index } => {
131                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
132                     return Err(format!(
133                         "{}: Received POINTER_DOWN but no pointers are currently down \
134                         for device {:?}",
135                         self.name, device_id
136                     ));
137                 }
138                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
139                 if it.len() != pointer_properties.len() - 1 {
140                     return Err(format!(
141                         "{}: There are currently {} touching pointers, but the incoming \
142                          POINTER_DOWN event has {}",
143                         self.name,
144                         it.len(),
145                         pointer_properties.len()
146                     ));
147                 }
148                 let pointer_id = pointer_properties[action_index].id;
149                 if it.contains(&pointer_id) {
150                     return Err(format!(
151                         "{}: Pointer with id={} already present found in the properties",
152                         self.name, pointer_id
153                     ));
154                 }
155                 it.insert(pointer_id);
156             }
157             MotionAction::Move => {
158                 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
159                     return Err(format!(
160                         "{}: ACTION_MOVE touching pointers don't match",
161                         self.name
162                     ));
163                 }
164             }
165             MotionAction::PointerUp { action_index } => {
166                 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
167                     return Err(format!(
168                         "{}: ACTION_POINTER_UP touching pointers don't match",
169                         self.name
170                     ));
171                 }
172                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
173                 let pointer_id = pointer_properties[action_index].id;
174                 it.remove(&pointer_id);
175             }
176             MotionAction::Up => {
177                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
178                     return Err(format!(
179                         "{} Received ACTION_UP but no pointers are currently down for device {:?}",
180                         self.name, device_id
181                     ));
182                 }
183                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
184                 if it.len() != 1 {
185                     return Err(format!(
186                         "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
187                         self.name, it, device_id
188                     ));
189                 }
190                 let pointer_id = pointer_properties[0].id;
191                 if !it.contains(&pointer_id) {
192                     return Err(format!(
193                         "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
194                         {:?} for device {:?}",
195                         self.name, pointer_id, it, device_id
196                     ));
197                 }
198                 self.touching_pointer_ids_by_device.remove(&device_id);
199             }
200             MotionAction::Cancel => {
201                 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
202                     return Err(format!(
203                         "{}: Got ACTION_CANCEL, but the pointers don't match. \
204                         Existing pointers: {:?}",
205                         self.name, self.touching_pointer_ids_by_device
206                     ));
207                 }
208                 self.touching_pointer_ids_by_device.remove(&device_id);
209             }
210             /*
211              * The hovering protocol currently supports a single pointer only, because we do not
212              * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
213              * Still, we are keeping the infrastructure here pretty general in case that is
214              * eventually supported.
215              */
216             MotionAction::HoverEnter => {
217                 if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
218                     return Err(format!(
219                         "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
220                         {:?}",
221                         self.name, device_id, self.hovering_pointer_ids_by_device
222                     ));
223                 }
224                 let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
225                 it.insert(pointer_properties[0].id);
226             }
227             MotionAction::HoverMove => {
228                 // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
229                 // If there was no prior HOVER_ENTER, just start a new hovering pointer.
230                 let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
231                 it.insert(pointer_properties[0].id);
232             }
233             MotionAction::HoverExit => {
234                 if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
235                     return Err(format!(
236                         "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
237                         self.name, device_id
238                     ));
239                 }
240                 let pointer_id = pointer_properties[0].id;
241                 let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
242                 it.remove(&pointer_id);
243 
244                 if !it.is_empty() {
245                     return Err(format!(
246                         "{}: Removed hovering pointer {}, but pointers are still\
247                                hovering for device {:?}: {:?}",
248                         self.name, pointer_id, device_id, it
249                     ));
250                 }
251                 self.hovering_pointer_ids_by_device.remove(&device_id);
252             }
253             _ => return Ok(()),
254         }
255         Ok(())
256     }
257 
258     /// Notify the verifier that the device has been reset, which will cause the verifier to erase
259     /// the current internal state for this device. Subsequent events from this device are expected
260     //// to start a new gesture.
reset_device(&mut self, device_id: DeviceId)261     pub fn reset_device(&mut self, device_id: DeviceId) {
262         self.touching_pointer_ids_by_device.remove(&device_id);
263         self.hovering_pointer_ids_by_device.remove(&device_id);
264     }
265 
ensure_touching_pointers_match( &self, device_id: DeviceId, pointer_properties: &[RustPointerProperties], ) -> bool266     fn ensure_touching_pointers_match(
267         &self,
268         device_id: DeviceId,
269         pointer_properties: &[RustPointerProperties],
270     ) -> bool {
271         let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
272             return false;
273         };
274 
275         if pointers.len() != pointer_properties.len() {
276             return false;
277         }
278 
279         for pointer_property in pointer_properties.iter() {
280             let pointer_id = pointer_property.id;
281             if !pointers.contains(&pointer_id) {
282                 return false;
283             }
284         }
285         true
286     }
287 }
288 
289 #[cfg(test)]
290 mod tests {
291     use crate::input_verifier::InputVerifier;
292     use crate::DeviceId;
293     use crate::MotionFlags;
294     use crate::RustPointerProperties;
295     use crate::Source;
296 
297     #[test]
298     /**
299      * Send a DOWN event with 2 pointers and ensure that it's marked as invalid.
300      */
bad_down_event()301     fn bad_down_event() {
302         let mut verifier = InputVerifier::new("Test", /*should_log*/ true);
303         let pointer_properties =
304             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
305         assert!(verifier
306             .process_movement(
307                 DeviceId(1),
308                 Source::Touchscreen,
309                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
310                 &pointer_properties,
311                 MotionFlags::empty(),
312             )
313             .is_err());
314     }
315 
316     #[test]
single_pointer_stream()317     fn single_pointer_stream() {
318         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
319         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
320         assert!(verifier
321             .process_movement(
322                 DeviceId(1),
323                 Source::Touchscreen,
324                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
325                 &pointer_properties,
326                 MotionFlags::empty(),
327             )
328             .is_ok());
329         assert!(verifier
330             .process_movement(
331                 DeviceId(1),
332                 Source::Touchscreen,
333                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
334                 &pointer_properties,
335                 MotionFlags::empty(),
336             )
337             .is_ok());
338         assert!(verifier
339             .process_movement(
340                 DeviceId(1),
341                 Source::Touchscreen,
342                 input_bindgen::AMOTION_EVENT_ACTION_UP,
343                 &pointer_properties,
344                 MotionFlags::empty(),
345             )
346             .is_ok());
347     }
348 
349     #[test]
two_pointer_stream()350     fn two_pointer_stream() {
351         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
352         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
353         assert!(verifier
354             .process_movement(
355                 DeviceId(1),
356                 Source::Touchscreen,
357                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
358                 &pointer_properties,
359                 MotionFlags::empty(),
360             )
361             .is_ok());
362         // POINTER 1 DOWN
363         let two_pointer_properties =
364             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
365         assert!(verifier
366             .process_movement(
367                 DeviceId(1),
368                 Source::Touchscreen,
369                 input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
370                     | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
371                 &two_pointer_properties,
372                 MotionFlags::empty(),
373             )
374             .is_ok());
375         // POINTER 0 UP
376         assert!(verifier
377             .process_movement(
378                 DeviceId(1),
379                 Source::Touchscreen,
380                 input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP
381                     | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
382                 &two_pointer_properties,
383                 MotionFlags::empty(),
384             )
385             .is_ok());
386         // ACTION_UP for pointer id=1
387         let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]);
388         assert!(verifier
389             .process_movement(
390                 DeviceId(1),
391                 Source::Touchscreen,
392                 input_bindgen::AMOTION_EVENT_ACTION_UP,
393                 &pointer_1_properties,
394                 MotionFlags::empty(),
395             )
396             .is_ok());
397     }
398 
399     #[test]
multi_device_stream()400     fn multi_device_stream() {
401         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
402         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
403         assert!(verifier
404             .process_movement(
405                 DeviceId(1),
406                 Source::Touchscreen,
407                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
408                 &pointer_properties,
409                 MotionFlags::empty(),
410             )
411             .is_ok());
412         assert!(verifier
413             .process_movement(
414                 DeviceId(1),
415                 Source::Touchscreen,
416                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
417                 &pointer_properties,
418                 MotionFlags::empty(),
419             )
420             .is_ok());
421         assert!(verifier
422             .process_movement(
423                 DeviceId(2),
424                 Source::Touchscreen,
425                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
426                 &pointer_properties,
427                 MotionFlags::empty(),
428             )
429             .is_ok());
430         assert!(verifier
431             .process_movement(
432                 DeviceId(2),
433                 Source::Touchscreen,
434                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
435                 &pointer_properties,
436                 MotionFlags::empty(),
437             )
438             .is_ok());
439         assert!(verifier
440             .process_movement(
441                 DeviceId(1),
442                 Source::Touchscreen,
443                 input_bindgen::AMOTION_EVENT_ACTION_UP,
444                 &pointer_properties,
445                 MotionFlags::empty(),
446             )
447             .is_ok());
448     }
449 
450     #[test]
action_cancel()451     fn action_cancel() {
452         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
453         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
454         assert!(verifier
455             .process_movement(
456                 DeviceId(1),
457                 Source::Touchscreen,
458                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
459                 &pointer_properties,
460                 MotionFlags::empty(),
461             )
462             .is_ok());
463         assert!(verifier
464             .process_movement(
465                 DeviceId(1),
466                 Source::Touchscreen,
467                 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
468                 &pointer_properties,
469                 MotionFlags::CANCELED,
470             )
471             .is_ok());
472     }
473 
474     #[test]
invalid_action_cancel()475     fn invalid_action_cancel() {
476         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
477         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
478         assert!(verifier
479             .process_movement(
480                 DeviceId(1),
481                 Source::Touchscreen,
482                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
483                 &pointer_properties,
484                 MotionFlags::empty(),
485             )
486             .is_ok());
487         assert!(verifier
488             .process_movement(
489                 DeviceId(1),
490                 Source::Touchscreen,
491                 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
492                 &pointer_properties,
493                 MotionFlags::empty(), // forgot to set FLAG_CANCELED
494             )
495             .is_err());
496     }
497 
498     #[test]
invalid_up()499     fn invalid_up() {
500         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
501         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
502         assert!(verifier
503             .process_movement(
504                 DeviceId(1),
505                 Source::Touchscreen,
506                 input_bindgen::AMOTION_EVENT_ACTION_UP,
507                 &pointer_properties,
508                 MotionFlags::empty(),
509             )
510             .is_err());
511     }
512 
513     #[test]
correct_hover_sequence()514     fn correct_hover_sequence() {
515         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
516         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
517         assert!(verifier
518             .process_movement(
519                 DeviceId(1),
520                 Source::Touchscreen,
521                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
522                 &pointer_properties,
523                 MotionFlags::empty(),
524             )
525             .is_ok());
526 
527         assert!(verifier
528             .process_movement(
529                 DeviceId(1),
530                 Source::Touchscreen,
531                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
532                 &pointer_properties,
533                 MotionFlags::empty(),
534             )
535             .is_ok());
536 
537         assert!(verifier
538             .process_movement(
539                 DeviceId(1),
540                 Source::Touchscreen,
541                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
542                 &pointer_properties,
543                 MotionFlags::empty(),
544             )
545             .is_ok());
546 
547         assert!(verifier
548             .process_movement(
549                 DeviceId(1),
550                 Source::Touchscreen,
551                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
552                 &pointer_properties,
553                 MotionFlags::empty(),
554             )
555             .is_ok());
556     }
557 
558     #[test]
double_hover_enter()559     fn double_hover_enter() {
560         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
561         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
562         assert!(verifier
563             .process_movement(
564                 DeviceId(1),
565                 Source::Touchscreen,
566                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
567                 &pointer_properties,
568                 MotionFlags::empty(),
569             )
570             .is_ok());
571 
572         assert!(verifier
573             .process_movement(
574                 DeviceId(1),
575                 Source::Touchscreen,
576                 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
577                 &pointer_properties,
578                 MotionFlags::empty(),
579             )
580             .is_err());
581     }
582 
583     // Send a MOVE without a preceding DOWN event. This is OK because it's from source
584     // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event.
585     #[test]
relative_mouse_move()586     fn relative_mouse_move() {
587         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
588         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
589         assert!(verifier
590             .process_movement(
591                 DeviceId(2),
592                 Source::MouseRelative,
593                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
594                 &pointer_properties,
595                 MotionFlags::empty(),
596             )
597             .is_ok());
598     }
599 
600     // Send a MOVE event with incorrect number of pointers (one of the pointers is missing).
601     #[test]
move_with_wrong_number_of_pointers()602     fn move_with_wrong_number_of_pointers() {
603         let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
604         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
605         assert!(verifier
606             .process_movement(
607                 DeviceId(1),
608                 Source::Touchscreen,
609                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
610                 &pointer_properties,
611                 MotionFlags::empty(),
612             )
613             .is_ok());
614         // POINTER 1 DOWN
615         let two_pointer_properties =
616             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
617         assert!(verifier
618             .process_movement(
619                 DeviceId(1),
620                 Source::Touchscreen,
621                 input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
622                     | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
623                 &two_pointer_properties,
624                 MotionFlags::empty(),
625             )
626             .is_ok());
627         // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
628         assert!(verifier
629             .process_movement(
630                 DeviceId(1),
631                 Source::Touchscreen,
632                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
633                 &pointer_properties,
634                 MotionFlags::empty(),
635             )
636             .is_err());
637     }
638 }
639