blob: fdb623033bdebe237e39baef67c2346c5106838b [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;
20use crate::input::{DeviceId, MotionAction, MotionFlags};
21use 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,
54 action: u32,
55 pointer_properties: &[RustPointerProperties],
56 flags: MotionFlags,
57 ) -> Result<(), String> {
58 if self.should_log {
59 info!(
60 "Processing {} for device {:?} ({} pointer{}) on {}",
61 MotionAction::from(action).to_string(),
62 device_id,
63 pointer_properties.len(),
64 if pointer_properties.len() == 1 { "" } else { "s" },
65 self.name
66 );
67 }
68
69 match action.into() {
70 MotionAction::Down => {
71 let it = self
72 .touching_pointer_ids_by_device
73 .entry(device_id)
74 .or_insert_with(HashSet::new);
75 let pointer_id = pointer_properties[0].id;
76 if it.contains(&pointer_id) {
77 return Err(format!(
78 "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
79 self.name, device_id, it
80 ));
81 }
82 it.insert(pointer_id);
83 }
84 MotionAction::PointerDown { action_index } => {
85 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
86 return Err(format!(
87 "{}: Received POINTER_DOWN but no pointers are currently down \
88 for device {:?}",
89 self.name, device_id
90 ));
91 }
92 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
93 let pointer_id = pointer_properties[action_index].id;
94 if it.contains(&pointer_id) {
95 return Err(format!(
96 "{}: Pointer with id={} not found in the properties",
97 self.name, pointer_id
98 ));
99 }
100 it.insert(pointer_id);
101 }
102 MotionAction::Move => {
103 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
104 return Err(format!(
105 "{}: ACTION_MOVE touching pointers don't match",
106 self.name
107 ));
108 }
109 }
110 MotionAction::PointerUp { action_index } => {
111 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
112 return Err(format!(
113 "{}: Received POINTER_UP but no pointers are currently down for device \
114 {:?}",
115 self.name, device_id
116 ));
117 }
118 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
119 let pointer_id = pointer_properties[action_index].id;
120 it.remove(&pointer_id);
121 }
122 MotionAction::Up => {
123 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
124 return Err(format!(
125 "{} Received ACTION_UP but no pointers are currently down for device {:?}",
126 self.name, device_id
127 ));
128 }
129 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
130 if it.len() != 1 {
131 return Err(format!(
132 "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
133 self.name, it, device_id
134 ));
135 }
136 let pointer_id = pointer_properties[0].id;
137 if !it.contains(&pointer_id) {
138 return Err(format!(
139 "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
140 {:?} for device {:?}",
141 self.name, pointer_id, it, device_id
142 ));
143 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700144 self.touching_pointer_ids_by_device.remove(&device_id);
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000145 }
146 MotionAction::Cancel => {
147 if flags.contains(MotionFlags::CANCELED) {
148 return Err(format!(
149 "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
150 self.name
151 ));
152 }
153 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
154 return Err(format!(
155 "{}: Got ACTION_CANCEL, but the pointers don't match. \
156 Existing pointers: {:?}",
157 self.name, self.touching_pointer_ids_by_device
158 ));
159 }
160 self.touching_pointer_ids_by_device.remove(&device_id);
161 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700162 /*
163 * The hovering protocol currently supports a single pointer only, because we do not
164 * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
165 * Still, we are keeping the infrastructure here pretty general in case that is
166 * eventually supported.
167 */
168 MotionAction::HoverEnter => {
169 if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
170 return Err(format!(
171 "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
172 {:?}",
173 self.name, device_id, self.hovering_pointer_ids_by_device
174 ));
175 }
176 let it = self
177 .hovering_pointer_ids_by_device
178 .entry(device_id)
179 .or_insert_with(HashSet::new);
180 it.insert(pointer_properties[0].id);
181 }
182 MotionAction::HoverMove => {
183 // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
184 // If there was no prior HOVER_ENTER, just start a new hovering pointer.
185 let it = self
186 .hovering_pointer_ids_by_device
187 .entry(device_id)
188 .or_insert_with(HashSet::new);
189 it.insert(pointer_properties[0].id);
190 }
191 MotionAction::HoverExit => {
192 if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
193 return Err(format!(
194 "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
195 self.name, device_id
196 ));
197 }
198 let pointer_id = pointer_properties[0].id;
199 let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
200 it.remove(&pointer_id);
201
202 if !it.is_empty() {
203 return Err(format!(
204 "{}: Removed hovering pointer {}, but pointers are still\
205 hovering for device {:?}: {:?}",
206 self.name, pointer_id, device_id, it
207 ));
208 }
209 self.hovering_pointer_ids_by_device.remove(&device_id);
210 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000211 _ => return Ok(()),
212 }
213 Ok(())
214 }
215
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700216 /// Notify the verifier that the device has been reset, which will cause the verifier to erase
217 /// the current internal state for this device. Subsequent events from this device are expected
218 //// to start a new gesture.
219 pub fn reset_device(&mut self, device_id: DeviceId) {
220 self.touching_pointer_ids_by_device.remove(&device_id);
221 self.hovering_pointer_ids_by_device.remove(&device_id);
222 }
223
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000224 fn ensure_touching_pointers_match(
225 &self,
226 device_id: DeviceId,
227 pointer_properties: &[RustPointerProperties],
228 ) -> bool {
229 let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
230 return false;
231 };
232
233 for pointer_property in pointer_properties.iter() {
234 let pointer_id = pointer_property.id;
235 if !pointers.contains(&pointer_id) {
236 return false;
237 }
238 }
239 true
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use crate::input_verifier::InputVerifier;
246 use crate::DeviceId;
247 use crate::MotionFlags;
248 use crate::RustPointerProperties;
249 #[test]
250 fn single_pointer_stream() {
251 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
252 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
253 assert!(verifier
254 .process_movement(
255 DeviceId(1),
256 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
257 &pointer_properties,
258 MotionFlags::empty(),
259 )
260 .is_ok());
261 assert!(verifier
262 .process_movement(
263 DeviceId(1),
264 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
265 &pointer_properties,
266 MotionFlags::empty(),
267 )
268 .is_ok());
269 assert!(verifier
270 .process_movement(
271 DeviceId(1),
272 input_bindgen::AMOTION_EVENT_ACTION_UP,
273 &pointer_properties,
274 MotionFlags::empty(),
275 )
276 .is_ok());
277 }
278
279 #[test]
280 fn multi_device_stream() {
281 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
282 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
283 assert!(verifier
284 .process_movement(
285 DeviceId(1),
286 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
287 &pointer_properties,
288 MotionFlags::empty(),
289 )
290 .is_ok());
291 assert!(verifier
292 .process_movement(
293 DeviceId(1),
294 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
295 &pointer_properties,
296 MotionFlags::empty(),
297 )
298 .is_ok());
299 assert!(verifier
300 .process_movement(
301 DeviceId(2),
302 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
303 &pointer_properties,
304 MotionFlags::empty(),
305 )
306 .is_ok());
307 assert!(verifier
308 .process_movement(
309 DeviceId(2),
310 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
311 &pointer_properties,
312 MotionFlags::empty(),
313 )
314 .is_ok());
315 assert!(verifier
316 .process_movement(
317 DeviceId(1),
318 input_bindgen::AMOTION_EVENT_ACTION_UP,
319 &pointer_properties,
320 MotionFlags::empty(),
321 )
322 .is_ok());
323 }
324
325 #[test]
326 fn test_invalid_up() {
327 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
328 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
329 assert!(verifier
330 .process_movement(
331 DeviceId(1),
332 input_bindgen::AMOTION_EVENT_ACTION_UP,
333 &pointer_properties,
334 MotionFlags::empty(),
335 )
336 .is_err());
337 }
Siarhei Vishniakou1160ecd2023-06-28 15:57:47 -0700338
339 #[test]
340 fn correct_hover_sequence() {
341 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
342 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
343 assert!(verifier
344 .process_movement(
345 DeviceId(1),
346 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
347 &pointer_properties,
348 MotionFlags::empty(),
349 )
350 .is_ok());
351
352 assert!(verifier
353 .process_movement(
354 DeviceId(1),
355 input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
356 &pointer_properties,
357 MotionFlags::empty(),
358 )
359 .is_ok());
360
361 assert!(verifier
362 .process_movement(
363 DeviceId(1),
364 input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
365 &pointer_properties,
366 MotionFlags::empty(),
367 )
368 .is_ok());
369
370 assert!(verifier
371 .process_movement(
372 DeviceId(1),
373 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
374 &pointer_properties,
375 MotionFlags::empty(),
376 )
377 .is_ok());
378 }
379
380 #[test]
381 fn double_hover_enter() {
382 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
383 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
384 assert!(verifier
385 .process_movement(
386 DeviceId(1),
387 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
388 &pointer_properties,
389 MotionFlags::empty(),
390 )
391 .is_ok());
392
393 assert!(verifier
394 .process_movement(
395 DeviceId(1),
396 input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
397 &pointer_properties,
398 MotionFlags::empty(),
399 )
400 .is_err());
401 }
Prabir Pradhan0762b1f2023-06-22 23:08:18 +0000402}