blob: 2d94e70309ba65ee14c1ff1f2228343e633aa17f [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
25/// The InputVerifier is used to validate a stream of input events.
26pub struct InputVerifier {
27 name: String,
28 should_log: bool,
29 touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -070030 hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000031}
32
33impl InputVerifier {
34 /// Create a new InputVerifier.
35 pub fn new(name: &str, should_log: bool) -> Self {
36 logger::init(
37 logger::Config::default()
38 .with_tag_on_device("InputVerifier")
39 .with_min_level(log::Level::Trace),
40 );
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -070041 Self {
42 name: name.to_owned(),
43 should_log,
44 touching_pointer_ids_by_device: HashMap::new(),
45 hovering_pointer_ids_by_device: HashMap::new(),
46 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000047 }
48
49 /// Process a pointer movement event from an InputDevice.
50 /// If the event is not valid, we return an error string that describes the issue.
51 pub fn process_movement(
52 &mut self,
53 device_id: DeviceId,
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -070054 source: Source,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000055 action: u32,
56 pointer_properties: &[RustPointerProperties],
57 flags: MotionFlags,
58 ) -> Result<(), String> {
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -070059 if !source.is_from_class(SourceClass::Pointer) {
60 // Skip non-pointer sources like MOUSE_RELATIVE for now
61 return Ok(());
62 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000063 if self.should_log {
64 info!(
65 "Processing {} for device {:?} ({} pointer{}) on {}",
66 MotionAction::from(action).to_string(),
67 device_id,
68 pointer_properties.len(),
69 if pointer_properties.len() == 1 { "" } else { "s" },
70 self.name
71 );
72 }
73
74 match action.into() {
75 MotionAction::Down => {
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -070076 if pointer_properties.len() != 1 {
77 return Err(format!(
78 "{}: Invalid DOWN event: there are {} pointers in the event",
79 self.name,
80 pointer_properties.len()
81 ));
82 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +000083 let it = self
84 .touching_pointer_ids_by_device
85 .entry(device_id)
86 .or_insert_with(HashSet::new);
87 let pointer_id = pointer_properties[0].id;
88 if it.contains(&pointer_id) {
89 return Err(format!(
90 "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
91 self.name, device_id, it
92 ));
93 }
94 it.insert(pointer_id);
95 }
96 MotionAction::PointerDown { action_index } => {
97 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
98 return Err(format!(
99 "{}: Received POINTER_DOWN but no pointers are currently down \
100 for device {:?}",
101 self.name, device_id
102 ));
103 }
104 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700105 if it.len() != pointer_properties.len() - 1 {
106 return Err(format!(
107 "{}: There are currently {} touching pointers, but the incoming \
108 POINTER_DOWN event has {}",
109 self.name,
110 it.len(),
111 pointer_properties.len()
112 ));
113 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000114 let pointer_id = pointer_properties[action_index].id;
115 if it.contains(&pointer_id) {
116 return Err(format!(
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700117 "{}: Pointer with id={} already present found in the properties",
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000118 self.name, pointer_id
119 ));
120 }
121 it.insert(pointer_id);
122 }
123 MotionAction::Move => {
124 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
125 return Err(format!(
126 "{}: ACTION_MOVE touching pointers don't match",
127 self.name
128 ));
129 }
130 }
131 MotionAction::PointerUp { action_index } => {
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700132 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000133 return Err(format!(
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700134 "{}: ACTION_POINTER_UP touching pointers don't match",
135 self.name
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000136 ));
137 }
138 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
139 let pointer_id = pointer_properties[action_index].id;
140 it.remove(&pointer_id);
141 }
142 MotionAction::Up => {
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700143 if pointer_properties.len() != 1 {
144 return Err(format!(
145 "{}: Invalid UP event: there are {} pointers in the event",
146 self.name,
147 pointer_properties.len()
148 ));
149 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000150 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
151 return Err(format!(
152 "{} Received ACTION_UP but no pointers are currently down for device {:?}",
153 self.name, device_id
154 ));
155 }
156 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
157 if it.len() != 1 {
158 return Err(format!(
159 "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
160 self.name, it, device_id
161 ));
162 }
163 let pointer_id = pointer_properties[0].id;
164 if !it.contains(&pointer_id) {
165 return Err(format!(
166 "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
167 {:?} for device {:?}",
168 self.name, pointer_id, it, device_id
169 ));
170 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700171 self.touching_pointer_ids_by_device.remove(&device_id);
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000172 }
173 MotionAction::Cancel => {
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700174 if !flags.contains(MotionFlags::CANCELED) {
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000175 return Err(format!(
176 "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
177 self.name
178 ));
179 }
180 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
181 return Err(format!(
182 "{}: Got ACTION_CANCEL, but the pointers don't match. \
183 Existing pointers: {:?}",
184 self.name, self.touching_pointer_ids_by_device
185 ));
186 }
187 self.touching_pointer_ids_by_device.remove(&device_id);
188 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700189 /*
190 * The hovering protocol currently supports a single pointer only, because we do not
191 * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
192 * Still, we are keeping the infrastructure here pretty general in case that is
193 * eventually supported.
194 */
195 MotionAction::HoverEnter => {
196 if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
197 return Err(format!(
198 "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
199 {:?}",
200 self.name, device_id, self.hovering_pointer_ids_by_device
201 ));
202 }
203 let it = self
204 .hovering_pointer_ids_by_device
205 .entry(device_id)
206 .or_insert_with(HashSet::new);
207 it.insert(pointer_properties[0].id);
208 }
209 MotionAction::HoverMove => {
210 // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
211 // If there was no prior HOVER_ENTER, just start a new hovering pointer.
212 let it = self
213 .hovering_pointer_ids_by_device
214 .entry(device_id)
215 .or_insert_with(HashSet::new);
216 it.insert(pointer_properties[0].id);
217 }
218 MotionAction::HoverExit => {
219 if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
220 return Err(format!(
221 "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
222 self.name, device_id
223 ));
224 }
225 let pointer_id = pointer_properties[0].id;
226 let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
227 it.remove(&pointer_id);
228
229 if !it.is_empty() {
230 return Err(format!(
231 "{}: Removed hovering pointer {}, but pointers are still\
232 hovering for device {:?}: {:?}",
233 self.name, pointer_id, device_id, it
234 ));
235 }
236 self.hovering_pointer_ids_by_device.remove(&device_id);
237 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000238 _ => return Ok(()),
239 }
240 Ok(())
241 }
242
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700243 /// Notify the verifier that the device has been reset, which will cause the verifier to erase
244 /// the current internal state for this device. Subsequent events from this device are expected
245 //// to start a new gesture.
246 pub fn reset_device(&mut self, device_id: DeviceId) {
247 self.touching_pointer_ids_by_device.remove(&device_id);
248 self.hovering_pointer_ids_by_device.remove(&device_id);
249 }
250
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000251 fn ensure_touching_pointers_match(
252 &self,
253 device_id: DeviceId,
254 pointer_properties: &[RustPointerProperties],
255 ) -> bool {
256 let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
257 return false;
258 };
259
260 for pointer_property in pointer_properties.iter() {
261 let pointer_id = pointer_property.id;
262 if !pointers.contains(&pointer_id) {
263 return false;
264 }
265 }
266 true
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use crate::input_verifier::InputVerifier;
273 use crate::DeviceId;
274 use crate::MotionFlags;
275 use crate::RustPointerProperties;
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700276 use crate::Source;
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000277 #[test]
278 fn single_pointer_stream() {
279 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
280 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
281 assert!(verifier
282 .process_movement(
283 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700284 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000285 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
286 &pointer_properties,
287 MotionFlags::empty(),
288 )
289 .is_ok());
290 assert!(verifier
291 .process_movement(
292 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700293 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000294 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
295 &pointer_properties,
296 MotionFlags::empty(),
297 )
298 .is_ok());
299 assert!(verifier
300 .process_movement(
301 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700302 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000303 input_bindgen::AMOTION_EVENT_ACTION_UP,
304 &pointer_properties,
305 MotionFlags::empty(),
306 )
307 .is_ok());
308 }
309
310 #[test]
311 fn multi_device_stream() {
312 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
313 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
314 assert!(verifier
315 .process_movement(
316 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700317 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000318 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
319 &pointer_properties,
320 MotionFlags::empty(),
321 )
322 .is_ok());
323 assert!(verifier
324 .process_movement(
325 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700326 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000327 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
328 &pointer_properties,
329 MotionFlags::empty(),
330 )
331 .is_ok());
332 assert!(verifier
333 .process_movement(
334 DeviceId(2),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700335 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000336 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
337 &pointer_properties,
338 MotionFlags::empty(),
339 )
340 .is_ok());
341 assert!(verifier
342 .process_movement(
343 DeviceId(2),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700344 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000345 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
346 &pointer_properties,
347 MotionFlags::empty(),
348 )
349 .is_ok());
350 assert!(verifier
351 .process_movement(
352 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700353 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000354 input_bindgen::AMOTION_EVENT_ACTION_UP,
355 &pointer_properties,
356 MotionFlags::empty(),
357 )
358 .is_ok());
359 }
360
361 #[test]
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700362 fn action_cancel() {
363 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
364 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
365 assert!(verifier
366 .process_movement(
367 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700368 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700369 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
370 &pointer_properties,
371 MotionFlags::empty(),
372 )
373 .is_ok());
374 assert!(verifier
375 .process_movement(
376 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700377 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700378 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
379 &pointer_properties,
380 MotionFlags::CANCELED,
381 )
382 .is_ok());
383 }
384
385 #[test]
386 fn invalid_action_cancel() {
387 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
388 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
389 assert!(verifier
390 .process_movement(
391 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700392 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700393 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
394 &pointer_properties,
395 MotionFlags::empty(),
396 )
397 .is_ok());
398 assert!(verifier
399 .process_movement(
400 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700401 Source::Touchscreen,
Siarhei Vishniakoue65fa752023-06-28 14:09:09 -0700402 input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
403 &pointer_properties,
404 MotionFlags::empty(), // forgot to set FLAG_CANCELED
405 )
406 .is_err());
407 }
408
409 #[test]
410 fn invalid_up() {
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000411 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
412 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
413 assert!(verifier
414 .process_movement(
415 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700416 Source::Touchscreen,
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000417 input_bindgen::AMOTION_EVENT_ACTION_UP,
418 &pointer_properties,
419 MotionFlags::empty(),
420 )
421 .is_err());
422 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700423
424 #[test]
425 fn correct_hover_sequence() {
426 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
427 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
428 assert!(verifier
429 .process_movement(
430 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700431 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700432 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
433 &pointer_properties,
434 MotionFlags::empty(),
435 )
436 .is_ok());
437
438 assert!(verifier
439 .process_movement(
440 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700441 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700442 input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
443 &pointer_properties,
444 MotionFlags::empty(),
445 )
446 .is_ok());
447
448 assert!(verifier
449 .process_movement(
450 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700451 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700452 input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
453 &pointer_properties,
454 MotionFlags::empty(),
455 )
456 .is_ok());
457
458 assert!(verifier
459 .process_movement(
460 DeviceId(1),
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700461 Source::Touchscreen,
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700462 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
463 &pointer_properties,
464 MotionFlags::empty(),
465 )
466 .is_ok());
467 }
468
469 #[test]
470 fn double_hover_enter() {
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_ENTER,
488 &pointer_properties,
489 MotionFlags::empty(),
490 )
491 .is_err());
492 }
Siarhei Vishniakou2d151ac2023-09-19 13:30:24 -0700493
494 // Send a MOVE without a preceding DOWN event. This is OK because it's from source
495 // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event.
496 #[test]
497 fn relative_mouse_move() {
498 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
499 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
500 assert!(verifier
501 .process_movement(
502 DeviceId(2),
503 Source::MouseRelative,
504 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
505 &pointer_properties,
506 MotionFlags::empty(),
507 )
508 .is_ok());
509 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000510}