blob: 1cc11297b6e25977c5594889c13b46c9db8c6b3e [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>>,
30}
31
32impl InputVerifier {
33 /// Create a new InputVerifier.
34 pub fn new(name: &str, should_log: bool) -> Self {
35 logger::init(
36 logger::Config::default()
37 .with_tag_on_device("InputVerifier")
38 .with_min_level(log::Level::Trace),
39 );
40 Self { name: name.to_owned(), should_log, touching_pointer_ids_by_device: HashMap::new() }
41 }
42
43 /// Process a pointer movement event from an InputDevice.
44 /// If the event is not valid, we return an error string that describes the issue.
45 pub fn process_movement(
46 &mut self,
47 device_id: DeviceId,
48 action: u32,
49 pointer_properties: &[RustPointerProperties],
50 flags: MotionFlags,
51 ) -> Result<(), String> {
52 if self.should_log {
53 info!(
54 "Processing {} for device {:?} ({} pointer{}) on {}",
55 MotionAction::from(action).to_string(),
56 device_id,
57 pointer_properties.len(),
58 if pointer_properties.len() == 1 { "" } else { "s" },
59 self.name
60 );
61 }
62
63 match action.into() {
64 MotionAction::Down => {
65 let it = self
66 .touching_pointer_ids_by_device
67 .entry(device_id)
68 .or_insert_with(HashSet::new);
69 let pointer_id = pointer_properties[0].id;
70 if it.contains(&pointer_id) {
71 return Err(format!(
72 "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
73 self.name, device_id, it
74 ));
75 }
76 it.insert(pointer_id);
77 }
78 MotionAction::PointerDown { action_index } => {
79 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
80 return Err(format!(
81 "{}: Received POINTER_DOWN but no pointers are currently down \
82 for device {:?}",
83 self.name, device_id
84 ));
85 }
86 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
87 let pointer_id = pointer_properties[action_index].id;
88 if it.contains(&pointer_id) {
89 return Err(format!(
90 "{}: Pointer with id={} not found in the properties",
91 self.name, pointer_id
92 ));
93 }
94 it.insert(pointer_id);
95 }
96 MotionAction::Move => {
97 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
98 return Err(format!(
99 "{}: ACTION_MOVE touching pointers don't match",
100 self.name
101 ));
102 }
103 }
104 MotionAction::PointerUp { action_index } => {
105 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
106 return Err(format!(
107 "{}: Received POINTER_UP but no pointers are currently down for device \
108 {:?}",
109 self.name, device_id
110 ));
111 }
112 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
113 let pointer_id = pointer_properties[action_index].id;
114 it.remove(&pointer_id);
115 }
116 MotionAction::Up => {
117 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
118 return Err(format!(
119 "{} Received ACTION_UP but no pointers are currently down for device {:?}",
120 self.name, device_id
121 ));
122 }
123 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
124 if it.len() != 1 {
125 return Err(format!(
126 "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
127 self.name, it, device_id
128 ));
129 }
130 let pointer_id = pointer_properties[0].id;
131 if !it.contains(&pointer_id) {
132 return Err(format!(
133 "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
134 {:?} for device {:?}",
135 self.name, pointer_id, it, device_id
136 ));
137 }
138 it.clear();
139 }
140 MotionAction::Cancel => {
141 if flags.contains(MotionFlags::CANCELED) {
142 return Err(format!(
143 "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
144 self.name
145 ));
146 }
147 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
148 return Err(format!(
149 "{}: Got ACTION_CANCEL, but the pointers don't match. \
150 Existing pointers: {:?}",
151 self.name, self.touching_pointer_ids_by_device
152 ));
153 }
154 self.touching_pointer_ids_by_device.remove(&device_id);
155 }
156 _ => return Ok(()),
157 }
158 Ok(())
159 }
160
161 fn ensure_touching_pointers_match(
162 &self,
163 device_id: DeviceId,
164 pointer_properties: &[RustPointerProperties],
165 ) -> bool {
166 let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
167 return false;
168 };
169
170 for pointer_property in pointer_properties.iter() {
171 let pointer_id = pointer_property.id;
172 if !pointers.contains(&pointer_id) {
173 return false;
174 }
175 }
176 true
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use crate::input_verifier::InputVerifier;
183 use crate::DeviceId;
184 use crate::MotionFlags;
185 use crate::RustPointerProperties;
186 #[test]
187 fn single_pointer_stream() {
188 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
189 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
190 assert!(verifier
191 .process_movement(
192 DeviceId(1),
193 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
194 &pointer_properties,
195 MotionFlags::empty(),
196 )
197 .is_ok());
198 assert!(verifier
199 .process_movement(
200 DeviceId(1),
201 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
202 &pointer_properties,
203 MotionFlags::empty(),
204 )
205 .is_ok());
206 assert!(verifier
207 .process_movement(
208 DeviceId(1),
209 input_bindgen::AMOTION_EVENT_ACTION_UP,
210 &pointer_properties,
211 MotionFlags::empty(),
212 )
213 .is_ok());
214 }
215
216 #[test]
217 fn multi_device_stream() {
218 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
219 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
220 assert!(verifier
221 .process_movement(
222 DeviceId(1),
223 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
224 &pointer_properties,
225 MotionFlags::empty(),
226 )
227 .is_ok());
228 assert!(verifier
229 .process_movement(
230 DeviceId(1),
231 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
232 &pointer_properties,
233 MotionFlags::empty(),
234 )
235 .is_ok());
236 assert!(verifier
237 .process_movement(
238 DeviceId(2),
239 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
240 &pointer_properties,
241 MotionFlags::empty(),
242 )
243 .is_ok());
244 assert!(verifier
245 .process_movement(
246 DeviceId(2),
247 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
248 &pointer_properties,
249 MotionFlags::empty(),
250 )
251 .is_ok());
252 assert!(verifier
253 .process_movement(
254 DeviceId(1),
255 input_bindgen::AMOTION_EVENT_ACTION_UP,
256 &pointer_properties,
257 MotionFlags::empty(),
258 )
259 .is_ok());
260 }
261
262 #[test]
263 fn test_invalid_up() {
264 let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
265 let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
266 assert!(verifier
267 .process_movement(
268 DeviceId(1),
269 input_bindgen::AMOTION_EVENT_ACTION_UP,
270 &pointer_properties,
271 MotionFlags::empty(),
272 )
273 .is_err());
274 }
275}