• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! Validate the incoming motion stream.
18 //! This class is not thread-safe.
19 //! State is stored in the "InputVerifier" object
20 //! that can be created via the 'create' method.
21 //! Usage:
22 //! Box<InputVerifier> verifier = create("inputChannel name");
23 //! result = process_movement(verifier, ...);
24 //! if (result) {
25 //!    crash(result.error_message());
26 //! }
27 
28 use std::collections::HashMap;
29 use std::collections::HashSet;
30 
31 use bitflags::bitflags;
32 use log::info;
33 
34 #[cxx::bridge(namespace = "android::input")]
35 #[allow(unsafe_op_in_unsafe_fn)]
36 mod ffi {
37     #[namespace = "android"]
38     unsafe extern "C++" {
39         include!("ffi/FromRustToCpp.h");
shouldLog(tag: &str) -> bool40         fn shouldLog(tag: &str) -> bool;
41     }
42     #[namespace = "android::input::verifier"]
43     extern "Rust" {
44         type InputVerifier;
45 
create(name: String) -> Box<InputVerifier>46         fn create(name: String) -> Box<InputVerifier>;
process_movement( verifier: &mut InputVerifier, device_id: i32, action: u32, pointer_properties: &[RustPointerProperties], flags: i32, ) -> String47         fn process_movement(
48             verifier: &mut InputVerifier,
49             device_id: i32,
50             action: u32,
51             pointer_properties: &[RustPointerProperties],
52             flags: i32,
53         ) -> String;
54     }
55 
56     pub struct RustPointerProperties {
57         id: i32,
58     }
59 }
60 
61 use crate::ffi::shouldLog;
62 use crate::ffi::RustPointerProperties;
63 
64 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
65 struct DeviceId(i32);
66 
process_movement( verifier: &mut InputVerifier, device_id: i32, action: u32, pointer_properties: &[RustPointerProperties], flags: i32, ) -> String67 fn process_movement(
68     verifier: &mut InputVerifier,
69     device_id: i32,
70     action: u32,
71     pointer_properties: &[RustPointerProperties],
72     flags: i32,
73 ) -> String {
74     let result = verifier.process_movement(
75         DeviceId(device_id),
76         action,
77         pointer_properties,
78         Flags::from_bits(flags).unwrap(),
79     );
80     match result {
81         Ok(()) => "".to_string(),
82         Err(e) => e,
83     }
84 }
85 
create(name: String) -> Box<InputVerifier>86 fn create(name: String) -> Box<InputVerifier> {
87     Box::new(InputVerifier::new(&name))
88 }
89 
90 #[repr(u32)]
91 enum MotionAction {
92     Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
93     Up = input_bindgen::AMOTION_EVENT_ACTION_UP,
94     Move = input_bindgen::AMOTION_EVENT_ACTION_MOVE,
95     Cancel = input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
96     Outside = input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE,
97     PointerDown { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN,
98     PointerUp { action_index: usize } = input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP,
99     HoverEnter = input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
100     HoverMove = input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
101     HoverExit = input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
102     Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
103     ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
104     ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
105 }
106 
get_action_index(action: u32) -> usize107 fn get_action_index(action: u32) -> usize {
108     let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
109         >> input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
110     index.try_into().unwrap()
111 }
112 
113 impl From<u32> for MotionAction {
from(action: u32) -> Self114     fn from(action: u32) -> Self {
115         let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
116         let action_index = get_action_index(action);
117         match action_masked {
118             input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
119             input_bindgen::AMOTION_EVENT_ACTION_UP => MotionAction::Up,
120             input_bindgen::AMOTION_EVENT_ACTION_MOVE => MotionAction::Move,
121             input_bindgen::AMOTION_EVENT_ACTION_CANCEL => MotionAction::Cancel,
122             input_bindgen::AMOTION_EVENT_ACTION_OUTSIDE => MotionAction::Outside,
123             input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN => {
124                 MotionAction::PointerDown { action_index }
125             }
126             input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP => {
127                 MotionAction::PointerUp { action_index }
128             }
129             input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER => MotionAction::HoverEnter,
130             input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
131             input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
132             input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
133             input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
134             input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
135             _ => panic!("Unknown action: {}", action),
136         }
137     }
138 }
139 
140 bitflags! {
141     struct Flags: i32 {
142         const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED;
143     }
144 }
145 
motion_action_to_string(action: u32) -> String146 fn motion_action_to_string(action: u32) -> String {
147     match action.into() {
148         MotionAction::Down => "DOWN".to_string(),
149         MotionAction::Up => "UP".to_string(),
150         MotionAction::Move => "MOVE".to_string(),
151         MotionAction::Cancel => "CANCEL".to_string(),
152         MotionAction::Outside => "OUTSIDE".to_string(),
153         MotionAction::PointerDown { action_index } => {
154             format!("POINTER_DOWN({})", action_index)
155         }
156         MotionAction::PointerUp { action_index } => {
157             format!("POINTER_UP({})", action_index)
158         }
159         MotionAction::HoverMove => "HOVER_MOVE".to_string(),
160         MotionAction::Scroll => "SCROLL".to_string(),
161         MotionAction::HoverEnter => "HOVER_ENTER".to_string(),
162         MotionAction::HoverExit => "HOVER_EXIT".to_string(),
163         MotionAction::ButtonPress => "BUTTON_PRESS".to_string(),
164         MotionAction::ButtonRelease => "BUTTON_RELEASE".to_string(),
165     }
166 }
167 
168 /**
169  * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead
170  * to inconsistent events.
171  * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG"
172  */
log_events() -> bool173 fn log_events() -> bool {
174     shouldLog("InputVerifierLogEvents")
175 }
176 
177 struct InputVerifier {
178     name: String,
179     touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
180 }
181 
182 impl InputVerifier {
new(name: &str) -> Self183     fn new(name: &str) -> Self {
184         logger::init(
185             logger::Config::default()
186                 .with_tag_on_device("InputVerifier")
187                 .with_min_level(log::Level::Trace),
188         );
189         Self { name: name.to_owned(), touching_pointer_ids_by_device: HashMap::new() }
190     }
191 
process_movement( &mut self, device_id: DeviceId, action: u32, pointer_properties: &[RustPointerProperties], flags: Flags, ) -> Result<(), String>192     fn process_movement(
193         &mut self,
194         device_id: DeviceId,
195         action: u32,
196         pointer_properties: &[RustPointerProperties],
197         flags: Flags,
198     ) -> Result<(), String> {
199         if log_events() {
200             info!(
201                 "Processing {} for device {:?} ({} pointer{}) on {}",
202                 motion_action_to_string(action),
203                 device_id,
204                 pointer_properties.len(),
205                 if pointer_properties.len() == 1 { "" } else { "s" },
206                 self.name
207             );
208         }
209 
210         match action.into() {
211             MotionAction::Down => {
212                 let it = self
213                     .touching_pointer_ids_by_device
214                     .entry(device_id)
215                     .or_insert_with(HashSet::new);
216                 let pointer_id = pointer_properties[0].id;
217                 if it.contains(&pointer_id) {
218                     return Err(format!(
219                         "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
220                         self.name, device_id, it
221                     ));
222                 }
223                 it.insert(pointer_id);
224             }
225             MotionAction::PointerDown { action_index } => {
226                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
227                     return Err(format!(
228                         "{}: Received POINTER_DOWN but no pointers are currently down \
229                         for device {:?}",
230                         self.name, device_id
231                     ));
232                 }
233                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
234                 let pointer_id = pointer_properties[action_index].id;
235                 if it.contains(&pointer_id) {
236                     return Err(format!(
237                         "{}: Pointer with id={} not found in the properties",
238                         self.name, pointer_id
239                     ));
240                 }
241                 it.insert(pointer_id);
242             }
243             MotionAction::Move => {
244                 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
245                     return Err(format!(
246                         "{}: ACTION_MOVE touching pointers don't match",
247                         self.name
248                     ));
249                 }
250             }
251             MotionAction::PointerUp { action_index } => {
252                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
253                     return Err(format!(
254                         "{}: Received POINTER_UP but no pointers are currently down for device \
255                         {:?}",
256                         self.name, device_id
257                     ));
258                 }
259                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
260                 let pointer_id = pointer_properties[action_index].id;
261                 it.remove(&pointer_id);
262             }
263             MotionAction::Up => {
264                 if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
265                     return Err(format!(
266                         "{} Received ACTION_UP but no pointers are currently down for device {:?}",
267                         self.name, device_id
268                     ));
269                 }
270                 let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
271                 if it.len() != 1 {
272                     return Err(format!(
273                         "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
274                         self.name, it, device_id
275                     ));
276                 }
277                 let pointer_id = pointer_properties[0].id;
278                 if !it.contains(&pointer_id) {
279                     return Err(format!(
280                         "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
281                         {:?} for device {:?}",
282                         self.name, pointer_id, it, device_id
283                     ));
284                 }
285                 it.clear();
286             }
287             MotionAction::Cancel => {
288                 if flags.contains(Flags::CANCELED) {
289                     return Err(format!(
290                         "{}: For ACTION_CANCEL, must set FLAG_CANCELED",
291                         self.name
292                     ));
293                 }
294                 if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
295                     return Err(format!(
296                         "{}: Got ACTION_CANCEL, but the pointers don't match. \
297                         Existing pointers: {:?}",
298                         self.name, self.touching_pointer_ids_by_device
299                     ));
300                 }
301                 self.touching_pointer_ids_by_device.remove(&device_id);
302             }
303             _ => return Ok(()),
304         }
305         Ok(())
306     }
307 
ensure_touching_pointers_match( &self, device_id: DeviceId, pointer_properties: &[RustPointerProperties], ) -> bool308     fn ensure_touching_pointers_match(
309         &self,
310         device_id: DeviceId,
311         pointer_properties: &[RustPointerProperties],
312     ) -> bool {
313         let Some(pointers) = self.touching_pointer_ids_by_device.get(&device_id) else {
314             return false;
315         };
316 
317         for pointer_property in pointer_properties.iter() {
318             let pointer_id = pointer_property.id;
319             if !pointers.contains(&pointer_id) {
320                 return false;
321             }
322         }
323         true
324     }
325 }
326 
327 #[cfg(test)]
328 mod tests {
329     use crate::DeviceId;
330     use crate::Flags;
331     use crate::InputVerifier;
332     use crate::RustPointerProperties;
333     #[test]
single_pointer_stream()334     fn single_pointer_stream() {
335         let mut verifier = InputVerifier::new("Test");
336         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
337         assert!(verifier
338             .process_movement(
339                 DeviceId(1),
340                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
341                 &pointer_properties,
342                 Flags::empty(),
343             )
344             .is_ok());
345         assert!(verifier
346             .process_movement(
347                 DeviceId(1),
348                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
349                 &pointer_properties,
350                 Flags::empty(),
351             )
352             .is_ok());
353         assert!(verifier
354             .process_movement(
355                 DeviceId(1),
356                 input_bindgen::AMOTION_EVENT_ACTION_UP,
357                 &pointer_properties,
358                 Flags::empty(),
359             )
360             .is_ok());
361     }
362 
363     #[test]
multi_device_stream()364     fn multi_device_stream() {
365         let mut verifier = InputVerifier::new("Test");
366         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
367         assert!(verifier
368             .process_movement(
369                 DeviceId(1),
370                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
371                 &pointer_properties,
372                 Flags::empty(),
373             )
374             .is_ok());
375         assert!(verifier
376             .process_movement(
377                 DeviceId(1),
378                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
379                 &pointer_properties,
380                 Flags::empty(),
381             )
382             .is_ok());
383         assert!(verifier
384             .process_movement(
385                 DeviceId(2),
386                 input_bindgen::AMOTION_EVENT_ACTION_DOWN,
387                 &pointer_properties,
388                 Flags::empty(),
389             )
390             .is_ok());
391         assert!(verifier
392             .process_movement(
393                 DeviceId(2),
394                 input_bindgen::AMOTION_EVENT_ACTION_MOVE,
395                 &pointer_properties,
396                 Flags::empty(),
397             )
398             .is_ok());
399         assert!(verifier
400             .process_movement(
401                 DeviceId(1),
402                 input_bindgen::AMOTION_EVENT_ACTION_UP,
403                 &pointer_properties,
404                 Flags::empty(),
405             )
406             .is_ok());
407     }
408 
409     #[test]
test_invalid_up()410     fn test_invalid_up() {
411         let mut verifier = InputVerifier::new("Test");
412         let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
413         assert!(verifier
414             .process_movement(
415                 DeviceId(1),
416                 input_bindgen::AMOTION_EVENT_ACTION_UP,
417                 &pointer_properties,
418                 Flags::empty(),
419             )
420             .is_err());
421     }
422 }
423