diff options
author | 2023-09-19 13:30:24 -0700 | |
---|---|---|
committer | 2023-09-27 13:50:59 +0000 | |
commit | 2d151ac3e2f6280f541fd75a9578ee1ab13ea405 (patch) | |
tree | a249116abc04f19c56535e686de54df16e0af2aa | |
parent | a22dbac767b580ecb55a0adc93b0296b0f7cd432 (diff) |
InputVerifier: only check pointer sources
Check for the source inside InputVerifier. Sources like MOUSE_RELATIVE
could send ACTION_MOVE events without a prior DOWN event. Verifying such
streams is tricky, so let's simply skip such events for now.
Also in this CL, add some verifications to the number of pointers inside
the event.
Bug: 211379801
Test: enable event verification and run native tests
Test: atest inputflinger_tests libinput_tests
Change-Id: I3703ba57af7ede77712b91b7429ac46c0624a616
-rw-r--r-- | include/input/InputVerifier.h | 2 | ||||
-rw-r--r-- | libs/input/Android.bp | 22 | ||||
-rw-r--r-- | libs/input/InputTransport.cpp | 4 | ||||
-rw-r--r-- | libs/input/InputVerifier.cpp | 6 | ||||
-rw-r--r-- | libs/input/rust/input.rs | 56 | ||||
-rw-r--r-- | libs/input/rust/input_verifier.rs | 76 | ||||
-rw-r--r-- | libs/input/rust/lib.rs | 5 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 6 |
8 files changed, 161 insertions, 16 deletions
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h index b8574829f3..14dd463425 100644 --- a/include/input/InputVerifier.h +++ b/include/input/InputVerifier.h @@ -46,7 +46,7 @@ class InputVerifier { public: InputVerifier(const std::string& name); - android::base::Result<void> processMovement(int32_t deviceId, int32_t action, + android::base::Result<void> processMovement(int32_t deviceId, int32_t source, int32_t action, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, int32_t flags); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 36a01d37a5..ab4af1a7b6 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -90,6 +90,28 @@ rust_bindgen { "--allowlist-var=AMOTION_EVENT_ACTION_DOWN", "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT", "--allowlist-var=MAX_POINTER_ID", + "--allowlist-var=AINPUT_SOURCE_CLASS_NONE", + "--allowlist-var=AINPUT_SOURCE_CLASS_BUTTON", + "--allowlist-var=AINPUT_SOURCE_CLASS_POINTER", + "--allowlist-var=AINPUT_SOURCE_CLASS_NAVIGATION", + "--allowlist-var=AINPUT_SOURCE_CLASS_POSITION", + "--allowlist-var=AINPUT_SOURCE_CLASS_JOYSTICK", + "--allowlist-var=AINPUT_SOURCE_UNKNOWN", + "--allowlist-var=AINPUT_SOURCE_KEYBOARD", + "--allowlist-var=AINPUT_SOURCE_DPAD", + "--allowlist-var=AINPUT_SOURCE_GAMEPAD", + "--allowlist-var=AINPUT_SOURCE_TOUCHSCREEN", + "--allowlist-var=AINPUT_SOURCE_MOUSE", + "--allowlist-var=AINPUT_SOURCE_STYLUS", + "--allowlist-var=AINPUT_SOURCE_BLUETOOTH_STYLUS", + "--allowlist-var=AINPUT_SOURCE_TRACKBALL", + "--allowlist-var=AINPUT_SOURCE_MOUSE_RELATIVE", + "--allowlist-var=AINPUT_SOURCE_TOUCHPAD", + "--allowlist-var=AINPUT_SOURCE_TOUCH_NAVIGATION", + "--allowlist-var=AINPUT_SOURCE_JOYSTICK", + "--allowlist-var=AINPUT_SOURCE_HDMI", + "--allowlist-var=AINPUT_SOURCE_SENSOR", + "--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER", ], static_libs: [ diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 16000139f7..b5a823d76a 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -632,8 +632,8 @@ status_t InputPublisher::publishMotionEvent( MotionEvent::actionToString(action).c_str())); if (verifyEvents()) { Result<void> result = - mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties, - pointerCoords, flags); + mInputVerifier.processMovement(deviceId, source, action, pointerCount, + pointerProperties, pointerCoords, flags); if (!result.ok()) { LOG(FATAL) << "Bad stream: " << result.error(); } diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp index 6c602e031d..cec244539e 100644 --- a/libs/input/InputVerifier.cpp +++ b/libs/input/InputVerifier.cpp @@ -33,7 +33,7 @@ namespace android { InputVerifier::InputVerifier(const std::string& name) : mVerifier(android::input::verifier::create(rust::String::lossy(name))){}; -Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action, +Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, int32_t action, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, int32_t flags) { @@ -43,8 +43,8 @@ Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action, } rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()}; rust::String errorMessage = - android::input::verifier::process_movement(*mVerifier, deviceId, action, properties, - static_cast<uint32_t>(flags)); + android::input::verifier::process_movement(*mVerifier, deviceId, source, action, + properties, static_cast<uint32_t>(flags)); if (errorMessage.empty()) { return {}; } else { diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs index 9d3b38693a..9725b00212 100644 --- a/libs/input/rust/input.rs +++ b/libs/input/rust/input.rs @@ -23,6 +23,54 @@ use std::fmt; #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct DeviceId(pub i32); +#[repr(u32)] +pub enum SourceClass { + None = input_bindgen::AINPUT_SOURCE_CLASS_NONE, + Button = input_bindgen::AINPUT_SOURCE_CLASS_BUTTON, + Pointer = input_bindgen::AINPUT_SOURCE_CLASS_POINTER, + Navigation = input_bindgen::AINPUT_SOURCE_CLASS_NAVIGATION, + Position = input_bindgen::AINPUT_SOURCE_CLASS_POSITION, + Joystick = input_bindgen::AINPUT_SOURCE_CLASS_JOYSTICK, +} + +bitflags! { + /// Source of the input device or input events. + pub struct Source: u32 { + /// SOURCE_UNKNOWN + const Unknown = input_bindgen::AINPUT_SOURCE_UNKNOWN; + /// SOURCE_KEYBOARD + const Keyboard = input_bindgen::AINPUT_SOURCE_KEYBOARD; + /// SOURCE_DPAD + const Dpad = input_bindgen::AINPUT_SOURCE_DPAD; + /// SOURCE_GAMEPAD + const Gamepad = input_bindgen::AINPUT_SOURCE_GAMEPAD; + /// SOURCE_TOUCHSCREEN + const Touchscreen = input_bindgen::AINPUT_SOURCE_TOUCHSCREEN; + /// SOURCE_MOUSE + const Mouse = input_bindgen::AINPUT_SOURCE_MOUSE; + /// SOURCE_STYLUS + const Stylus = input_bindgen::AINPUT_SOURCE_STYLUS; + /// SOURCE_BLUETOOTH_STYLUS + const BluetoothStylus = input_bindgen::AINPUT_SOURCE_BLUETOOTH_STYLUS; + /// SOURCE_TRACKBALL + const Trackball = input_bindgen::AINPUT_SOURCE_TRACKBALL; + /// SOURCE_MOUSE_RELATIVE + const MouseRelative = input_bindgen::AINPUT_SOURCE_MOUSE_RELATIVE; + /// SOURCE_TOUCHPAD + const Touchpad = input_bindgen::AINPUT_SOURCE_TOUCHPAD; + /// SOURCE_TOUCH_NAVIGATION + const TouchNavigation = input_bindgen::AINPUT_SOURCE_TOUCH_NAVIGATION; + /// SOURCE_JOYSTICK + const Joystick = input_bindgen::AINPUT_SOURCE_JOYSTICK; + /// SOURCE_HDMI + const HDMI = input_bindgen::AINPUT_SOURCE_HDMI; + /// SOURCE_SENSOR + const Sensor = input_bindgen::AINPUT_SOURCE_SENSOR; + /// SOURCE_ROTARY_ENCODER + const RotaryEncoder = input_bindgen::AINPUT_SOURCE_ROTARY_ENCODER; + } +} + /// A rust enum representation of a MotionEvent action. #[repr(u32)] pub enum MotionAction { @@ -134,3 +182,11 @@ bitflags! { const NO_FOCUS_CHANGE = input_bindgen::AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE; } } + +impl Source { + /// Return true if this source is from the provided class + pub fn is_from_class(&self, source_class: SourceClass) -> bool { + let class_bits = source_class as u32; + self.bits() & class_bits == class_bits + } +} diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs index 64c0466687..2d94e70309 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -17,7 +17,7 @@ //! Contains the InputVerifier, used to validate a stream of input events. use crate::ffi::RustPointerProperties; -use crate::input::{DeviceId, MotionAction, MotionFlags}; +use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass}; use log::info; use std::collections::HashMap; use std::collections::HashSet; @@ -51,10 +51,15 @@ impl InputVerifier { pub fn process_movement( &mut self, device_id: DeviceId, + source: Source, action: u32, pointer_properties: &[RustPointerProperties], flags: MotionFlags, ) -> Result<(), String> { + if !source.is_from_class(SourceClass::Pointer) { + // Skip non-pointer sources like MOUSE_RELATIVE for now + return Ok(()); + } if self.should_log { info!( "Processing {} for device {:?} ({} pointer{}) on {}", @@ -68,6 +73,13 @@ impl InputVerifier { match action.into() { MotionAction::Down => { + if pointer_properties.len() != 1 { + return Err(format!( + "{}: Invalid DOWN event: there are {} pointers in the event", + self.name, + pointer_properties.len() + )); + } let it = self .touching_pointer_ids_by_device .entry(device_id) @@ -90,10 +102,19 @@ impl InputVerifier { )); } let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap(); + if it.len() != pointer_properties.len() - 1 { + return Err(format!( + "{}: There are currently {} touching pointers, but the incoming \ + POINTER_DOWN event has {}", + self.name, + it.len(), + pointer_properties.len() + )); + } let pointer_id = pointer_properties[action_index].id; if it.contains(&pointer_id) { return Err(format!( - "{}: Pointer with id={} not found in the properties", + "{}: Pointer with id={} already present found in the properties", self.name, pointer_id )); } @@ -108,11 +129,10 @@ impl InputVerifier { } } MotionAction::PointerUp { action_index } => { - if !self.touching_pointer_ids_by_device.contains_key(&device_id) { + if !self.ensure_touching_pointers_match(device_id, pointer_properties) { return Err(format!( - "{}: Received POINTER_UP but no pointers are currently down for device \ - {:?}", - self.name, device_id + "{}: ACTION_POINTER_UP touching pointers don't match", + self.name )); } let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap(); @@ -120,6 +140,13 @@ impl InputVerifier { it.remove(&pointer_id); } MotionAction::Up => { + if pointer_properties.len() != 1 { + return Err(format!( + "{}: Invalid UP event: there are {} pointers in the event", + self.name, + pointer_properties.len() + )); + } if !self.touching_pointer_ids_by_device.contains_key(&device_id) { return Err(format!( "{} Received ACTION_UP but no pointers are currently down for device {:?}", @@ -246,6 +273,7 @@ mod tests { use crate::DeviceId; use crate::MotionFlags; use crate::RustPointerProperties; + use crate::Source; #[test] fn single_pointer_stream() { let mut verifier = InputVerifier::new("Test", /*should_log*/ false); @@ -253,6 +281,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, MotionFlags::empty(), @@ -261,6 +290,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, &pointer_properties, MotionFlags::empty(), @@ -269,6 +299,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, &pointer_properties, MotionFlags::empty(), @@ -283,6 +314,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, MotionFlags::empty(), @@ -291,6 +323,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, &pointer_properties, MotionFlags::empty(), @@ -299,6 +332,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(2), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, MotionFlags::empty(), @@ -307,6 +341,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(2), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, &pointer_properties, MotionFlags::empty(), @@ -315,6 +350,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, &pointer_properties, MotionFlags::empty(), @@ -329,6 +365,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, MotionFlags::empty(), @@ -337,6 +374,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_CANCEL, &pointer_properties, MotionFlags::CANCELED, @@ -351,6 +389,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, &pointer_properties, MotionFlags::empty(), @@ -359,6 +398,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_CANCEL, &pointer_properties, MotionFlags::empty(), // forgot to set FLAG_CANCELED @@ -373,6 +413,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, &pointer_properties, MotionFlags::empty(), @@ -387,6 +428,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, &pointer_properties, MotionFlags::empty(), @@ -396,6 +438,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, &pointer_properties, MotionFlags::empty(), @@ -405,6 +448,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT, &pointer_properties, MotionFlags::empty(), @@ -414,6 +458,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, &pointer_properties, MotionFlags::empty(), @@ -428,6 +473,7 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, &pointer_properties, MotionFlags::empty(), @@ -437,10 +483,28 @@ mod tests { assert!(verifier .process_movement( DeviceId(1), + Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, &pointer_properties, MotionFlags::empty(), ) .is_err()); } + + // Send a MOVE without a preceding DOWN event. This is OK because it's from source + // MOUSE_RELATIVE, which is used during pointer capture. The verifier should allow such event. + #[test] + fn relative_mouse_move() { + let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(2), + Source::MouseRelative, + input_bindgen::AMOTION_EVENT_ACTION_MOVE, + &pointer_properties, + MotionFlags::empty(), + ) + .is_ok()); + } } diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 1d3c434f76..01d959942c 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -19,7 +19,7 @@ mod input; mod input_verifier; -pub use input::{DeviceId, MotionAction, MotionFlags}; +pub use input::{DeviceId, MotionAction, MotionFlags, Source}; pub use input_verifier::InputVerifier; #[cxx::bridge(namespace = "android::input")] @@ -51,6 +51,7 @@ mod ffi { fn process_movement( verifier: &mut InputVerifier, device_id: i32, + source: u32, action: u32, pointer_properties: &[RustPointerProperties], flags: u32, @@ -73,12 +74,14 @@ fn create(name: String) -> Box<InputVerifier> { fn process_movement( verifier: &mut InputVerifier, device_id: i32, + source: u32, action: u32, pointer_properties: &[RustPointerProperties], flags: u32, ) -> String { let result = verifier.process_movement( DeviceId(device_id), + Source::from_bits(source).unwrap(), action, pointer_properties, MotionFlags::from_bits(flags).unwrap(), diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 9cd1e0903d..4657c011f6 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4324,9 +4324,9 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { mVerifiersByDisplay.try_emplace(args.displayId, StringPrintf("display %" PRId32, args.displayId)); Result<void> result = - it->second.processMovement(args.deviceId, args.action, args.getPointerCount(), - args.pointerProperties.data(), args.pointerCoords.data(), - args.flags); + it->second.processMovement(args.deviceId, args.source, args.action, + args.getPointerCount(), args.pointerProperties.data(), + args.pointerCoords.data(), args.flags); if (!result.ok()) { LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump(); } |