diff options
author | 2025-01-27 09:01:06 -0800 | |
---|---|---|
committer | 2025-02-05 12:44:25 +0000 | |
commit | 26640c99888043d5f41b5e209bb96efe76b7ef9e (patch) | |
tree | fb4bf31b87904bd9d917042959faecd0812fe565 | |
parent | 03ec059e19d131dc3ea20504f7c0372eddb26d91 (diff) |
InputVerifier: verify button events and states (attempt 2)
Check the following things:
* BUTTON_PRESS and _RELEASE actions have a single, valid action button
* No redundant BUTTON_PRESS or _RELEASE actions (i.e. for buttons that
are already pressed or released)
* Button state remains consistent throughout the stream, i.e.:
* Buttons are only added to the state by BUTTON_PRESS (though DOWN
events can have "pending" buttons on them)
* Buttons are only removed from the state by BUTTON_RELEASE
* When a DOWN event has pending buttons in its state, it is
immediately followed by a BUTTON_PRESS for each one
We could also verify that press and release events for primary,
secondary, and tertiary buttons are accompanied by down and up events.
However, I couldn't find any documentation that states which buttons
should result in down or up events, so I haven't implemented this for
now.
This is the second attempt to land this change, due to the original
causing test failures. Change I5c259c13d433d3010d2cf9c6fe01e08ba5990ef7
fixes the failures. v2 also adds a separate flag for button state
verification, as it is actually used in production to check events
injected by assistive technologies, whether or not the flag that I
previously thought was guarding it is enabled.
Test: connect a mouse and a touchpad, enable the verifier, play around
with the buttons, and check that any issues found by the verifier
appear to be legitimate. (I found b/391298464 , and checked that
the verifier caught a button problem with a partial fix to
b/372571823 .)
Test: atest --host libinput_rust_tests
Test: atest --host frameworks/native/libs/input/tests/InputVerifier_test.cpp
Test: atest --host \
frameworks/native/services/inputflinger/tests/InputDispatcher_test.cpp
Bug: 392870542
Flag: com.android.input.flags.enable_button_state_verification
Change-Id: I46f489b26df8785563e41e58135b6b5de4ff62a2
-rw-r--r-- | include/android/input.h | 2 | ||||
-rw-r--r-- | include/input/InputVerifier.h | 5 | ||||
-rw-r--r-- | libs/input/Android.bp | 7 | ||||
-rw-r--r-- | libs/input/InputTransport.cpp | 6 | ||||
-rw-r--r-- | libs/input/InputVerifier.cpp | 17 | ||||
-rw-r--r-- | libs/input/input_flags.aconfig | 10 | ||||
-rw-r--r-- | libs/input/rust/input.rs | 22 | ||||
-rw-r--r-- | libs/input/rust/input_verifier.rs | 706 | ||||
-rw-r--r-- | libs/input/rust/lib.rs | 32 | ||||
-rw-r--r-- | libs/input/tests/InputVerifier_test.cpp | 4 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 10 | ||||
-rw-r--r-- | services/inputflinger/reader/mapper/CursorInputMapper.h | 1 | ||||
-rw-r--r-- | services/inputflinger/tests/GestureConverter_test.cpp | 13 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 70 | ||||
-rw-r--r-- | services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp | 4 |
15 files changed, 856 insertions, 53 deletions
diff --git a/include/android/input.h b/include/android/input.h index ee98d7aee9..5f445509fa 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -849,6 +849,7 @@ enum { * Refer to the documentation on the MotionEvent class for descriptions of each button. */ enum { + // LINT.IfChange(AMOTION_EVENT_BUTTON) /** primary */ AMOTION_EVENT_BUTTON_PRIMARY = 1 << 0, /** secondary */ @@ -861,6 +862,7 @@ enum { AMOTION_EVENT_BUTTON_FORWARD = 1 << 4, AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5, AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6, + // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs) }; /** diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h index 14dd463425..7d3fb469c6 100644 --- a/include/input/InputVerifier.h +++ b/include/input/InputVerifier.h @@ -47,9 +47,10 @@ public: InputVerifier(const std::string& name); android::base::Result<void> processMovement(int32_t deviceId, int32_t source, int32_t action, - uint32_t pointerCount, + int32_t actionButton, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, int32_t flags); + const PointerCoords* pointerCoords, int32_t flags, + int32_t buttonState); void resetDevice(int32_t deviceId); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index cd6fe90a62..d2e432063a 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -133,6 +133,13 @@ rust_bindgen { "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN", "--allowlist-var=AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT", "--allowlist-var=AMOTION_EVENT_ACTION_UP", + "--allowlist-var=AMOTION_EVENT_BUTTON_BACK", + "--allowlist-var=AMOTION_EVENT_BUTTON_FORWARD", + "--allowlist-var=AMOTION_EVENT_BUTTON_PRIMARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_SECONDARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_STYLUS_PRIMARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_STYLUS_SECONDARY", + "--allowlist-var=AMOTION_EVENT_BUTTON_TERTIARY", "--allowlist-var=MAX_POINTER_ID", "--verbose", ], diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 56ccaab9ad..d388d48e8d 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -651,9 +651,9 @@ status_t InputPublisher::publishMotionEvent( const status_t status = mChannel->sendMessage(&msg); if (status == OK && verifyEvents()) { - Result<void> result = - mInputVerifier.processMovement(deviceId, source, action, pointerCount, - pointerProperties, pointerCoords, flags); + Result<void> result = mInputVerifier.processMovement(deviceId, source, action, actionButton, + pointerCount, pointerProperties, + pointerCoords, flags, buttonState); if (!result.ok()) { LOG(ERROR) << "Bad stream: " << result.error(); return BAD_VALUE; diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp index cec244539e..7811acefd0 100644 --- a/libs/input/InputVerifier.cpp +++ b/libs/input/InputVerifier.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "InputVerifier" #include <android-base/logging.h> +#include <com_android_input_flags.h> #include <input/InputVerifier.h> #include "input_cxx_bridge.rs.h" @@ -26,17 +27,23 @@ using android::input::RustPointerProperties; using DeviceId = int32_t; +namespace input_flags = com::android::input::flags; + namespace android { // --- InputVerifier --- InputVerifier::InputVerifier(const std::string& name) - : mVerifier(android::input::verifier::create(rust::String::lossy(name))){}; + : mVerifier( + android::input::verifier::create(rust::String::lossy(name), + input_flags::enable_button_state_verification())) { +} Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, int32_t action, - uint32_t pointerCount, + int32_t actionButton, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, int32_t flags) { + const PointerCoords* pointerCoords, int32_t flags, + int32_t buttonState) { std::vector<RustPointerProperties> rpp; for (size_t i = 0; i < pointerCount; i++) { rpp.emplace_back(RustPointerProperties{.id = pointerProperties[i].id}); @@ -44,7 +51,9 @@ Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t source, i rust::Slice<const RustPointerProperties> properties{rpp.data(), rpp.size()}; rust::String errorMessage = android::input::verifier::process_movement(*mVerifier, deviceId, source, action, - properties, static_cast<uint32_t>(flags)); + actionButton, properties, + static_cast<uint32_t>(flags), + static_cast<uint32_t>(buttonState)); if (errorMessage.empty()) { return {}; } else { diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 72a6fdf1bb..4e187ed333 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -16,6 +16,16 @@ flag { } flag { + name: "enable_button_state_verification" + namespace: "input" + description: "Set to true to enable crashing whenever bad inbound events are going into InputDispatcher" + bug: "392870542" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "remove_input_channel_from_windowstate" namespace: "input" description: "Do not store a copy of input channel inside WindowState." diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs index 6956a84d40..6eb2d73b1f 100644 --- a/libs/input/rust/input.rs +++ b/libs/input/rust/input.rs @@ -101,6 +101,7 @@ bitflags! { /// A rust enum representation of a MotionEvent action. #[repr(u32)] +#[derive(Eq, PartialEq)] pub enum MotionAction { /// ACTION_DOWN Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN, @@ -194,6 +195,27 @@ impl MotionAction { } bitflags! { + /// MotionEvent buttons. + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] + pub struct MotionButton: u32 { + /// Primary button (e.g. the left mouse button) + const Primary = input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY; + /// Secondary button (e.g. the right mouse button) + const Secondary = input_bindgen::AMOTION_EVENT_BUTTON_SECONDARY; + /// Tertiary button (e.g. the middle mouse button) + const Tertiary = input_bindgen::AMOTION_EVENT_BUTTON_TERTIARY; + /// Back button + const Back = input_bindgen::AMOTION_EVENT_BUTTON_BACK; + /// Forward button + const Forward = input_bindgen::AMOTION_EVENT_BUTTON_FORWARD; + /// Primary stylus button + const StylusPrimary = input_bindgen::AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + /// Secondary stylus button + const StylusSecondary = input_bindgen::AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; + } +} + +bitflags! { /// MotionEvent flags. /// The source of truth for the flag definitions are the MotionEventFlag AIDL enum. /// The flag values are redefined here as a bitflags API. diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs index bddd2a7927..6d94316272 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -17,20 +17,30 @@ //! Contains the InputVerifier, used to validate a stream of input events. use crate::ffi::RustPointerProperties; -use crate::input::{DeviceId, MotionAction, MotionFlags, Source, SourceClass}; +use crate::input::{DeviceId, MotionAction, MotionButton, MotionFlags, Source, SourceClass}; use log::info; use std::collections::HashMap; use std::collections::HashSet; fn verify_event( action: MotionAction, + action_button: MotionButton, pointer_properties: &[RustPointerProperties], flags: &MotionFlags, + verify_buttons: bool, ) -> Result<(), String> { let pointer_count = pointer_properties.len(); if pointer_count < 1 { return Err(format!("Invalid {} event: no pointers", action)); } + if action_button != MotionButton::empty() + && action != MotionAction::ButtonPress + && action != MotionAction::ButtonRelease + { + return Err(format!( + "Invalid {action} event: has action button {action_button:?} but is not a button action" + )); + } match action { MotionAction::Down | MotionAction::HoverEnter @@ -58,22 +68,126 @@ fn verify_event( } } + MotionAction::ButtonPress | MotionAction::ButtonRelease => { + if verify_buttons { + let button_count = action_button.iter().count(); + if button_count != 1 { + return Err(format!( + "Invalid {action} event: must specify a single action button, not \ + {button_count} action buttons" + )); + } + } + } + _ => {} } Ok(()) } +/// Keeps track of the button state for a single device and verifies events against it. +#[derive(Default)] +struct ButtonVerifier { + /// The current button state of the device. + button_state: MotionButton, + + /// The set of "pending buttons", which were seen in the last DOWN event's button state but + /// for which we haven't seen BUTTON_PRESS events yet. + /// + /// We allow DOWN events to include buttons in their state for which BUTTON_PRESS events haven't + /// been sent yet. In that case, the DOWN should be immediately followed by BUTTON_PRESS events + /// for those buttons, building up to a button state matching that of the DOWN. For example, if + /// the user presses the primary and secondary buttons at exactly the same time, we'd expect + /// this sequence: + /// + /// | Action | Action button | Button state | + /// |----------------|---------------|------------------------| + /// | `HOVER_EXIT` | - | - | + /// | `DOWN` | - | `PRIMARY`, `SECONDARY` | + /// | `BUTTON_PRESS` | `PRIMARY` | `PRIMARY` | + /// | `BUTTON_PRESS` | `SECONDARY` | `PRIMARY`, `SECONDARY` | + /// | `MOVE` | - | `PRIMARY`, `SECONDARY` | + pending_buttons: MotionButton, +} + +impl ButtonVerifier { + pub fn process_action( + &mut self, + action: MotionAction, + action_button: MotionButton, + button_state: MotionButton, + ) -> Result<(), String> { + if !self.pending_buttons.is_empty() { + // We just saw a DOWN with some additional buttons in its state, so it should be + // immediately followed by ButtonPress events for those buttons. + if action != MotionAction::ButtonPress || !self.pending_buttons.contains(action_button) + { + return Err(format!( + "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {} with \ + action button {:?}", + self.pending_buttons, action, action_button + )); + } else { + self.pending_buttons -= action_button; + } + } + let expected_state = match action { + MotionAction::Down => { + if self.button_state - button_state != MotionButton::empty() { + return Err(format!( + "DOWN event button state is missing {:?}", + self.button_state - button_state + )); + } + self.pending_buttons = button_state - self.button_state; + // We've already checked that the state isn't missing any already-down buttons, and + // extra buttons are valid on DOWN actions, so bypass the expected state check. + button_state + } + MotionAction::ButtonPress => { + if self.button_state.contains(action_button) { + return Err(format!( + "Duplicate BUTTON_PRESS; button state already contains {action_button:?}" + )); + } + self.button_state | action_button + } + MotionAction::ButtonRelease => { + if !self.button_state.contains(action_button) { + return Err(format!( + "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}" + )); + } + self.button_state - action_button + } + _ => self.button_state, + }; + if button_state != expected_state { + return Err(format!( + "Expected {action} button state to be {expected_state:?}, but was {button_state:?}" + )); + } + // DOWN events can have pending buttons, so don't update the state for them. + if action != MotionAction::Down { + self.button_state = button_state; + } + Ok(()) + } +} + /// The InputVerifier is used to validate a stream of input events. pub struct InputVerifier { name: String, should_log: bool, + verify_buttons: bool, touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>, hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>, + button_verifier_by_device: HashMap<DeviceId, ButtonVerifier>, } impl InputVerifier { /// Create a new InputVerifier. - pub fn new(name: &str, should_log: bool) -> Self { + pub fn new(name: &str, should_log: bool, verify_buttons: bool) -> Self { logger::init( logger::Config::default() .with_tag_on_device("InputVerifier") @@ -82,20 +196,25 @@ impl InputVerifier { Self { name: name.to_owned(), should_log, + verify_buttons, touching_pointer_ids_by_device: HashMap::new(), hovering_pointer_ids_by_device: HashMap::new(), + button_verifier_by_device: HashMap::new(), } } /// Process a pointer movement event from an InputDevice. /// If the event is not valid, we return an error string that describes the issue. + #[allow(clippy::too_many_arguments)] pub fn process_movement( &mut self, device_id: DeviceId, source: Source, action: u32, + action_button: MotionButton, pointer_properties: &[RustPointerProperties], flags: MotionFlags, + button_state: MotionButton, ) -> Result<(), String> { if !source.is_from_class(SourceClass::Pointer) { // Skip non-pointer sources like MOUSE_RELATIVE for now @@ -112,7 +231,21 @@ impl InputVerifier { ); } - verify_event(action.into(), pointer_properties, &flags)?; + verify_event( + action.into(), + action_button, + pointer_properties, + &flags, + self.verify_buttons, + )?; + + if self.verify_buttons { + self.button_verifier_by_device.entry(device_id).or_default().process_action( + action.into(), + action_button, + button_state, + )?; + } match action.into() { MotionAction::Down => { @@ -286,6 +419,7 @@ impl InputVerifier { #[cfg(test)] mod tests { + use crate::input::MotionButton; use crate::input_verifier::InputVerifier; use crate::DeviceId; use crate::MotionFlags; @@ -297,7 +431,8 @@ mod tests { * Send a DOWN event with 2 pointers and ensure that it's marked as invalid. */ fn bad_down_event() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ true); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ true, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]); assert!(verifier @@ -305,23 +440,28 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_err()); } #[test] fn single_pointer_stream() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -329,8 +469,10 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -338,23 +480,28 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); } #[test] fn two_pointer_stream() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); // POINTER 1 DOWN @@ -366,8 +513,10 @@ mod tests { Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + MotionButton::empty(), &two_pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); // POINTER 0 UP @@ -377,8 +526,10 @@ mod tests { Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + MotionButton::empty(), &two_pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); // ACTION_UP for pointer id=1 @@ -388,23 +539,28 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, + MotionButton::empty(), &pointer_1_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); } #[test] fn multi_device_stream() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -412,8 +568,10 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -421,8 +579,10 @@ mod tests { DeviceId(2), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -430,8 +590,10 @@ mod tests { DeviceId(2), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -439,23 +601,28 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); } #[test] fn action_cancel() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -463,23 +630,28 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_CANCEL, + MotionButton::empty(), &pointer_properties, MotionFlags::CANCELED, + MotionButton::empty(), ) .is_ok()); } #[test] fn invalid_action_cancel() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); assert!(verifier @@ -487,38 +659,46 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_CANCEL, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), // forgot to set FLAG_CANCELED + MotionButton::empty(), ) .is_err()); } #[test] fn invalid_up() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_UP, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_err()); } #[test] fn correct_hover_sequence() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); @@ -527,8 +707,10 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); @@ -537,8 +719,10 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); @@ -547,23 +731,28 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); } #[test] fn double_hover_enter() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); @@ -572,8 +761,10 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_err()); } @@ -582,15 +773,18 @@ mod tests { // 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 mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(2), Source::MouseRelative, input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); } @@ -598,15 +792,18 @@ mod tests { // Send a MOVE event with incorrect number of pointers (one of the pointers is missing). #[test] fn move_with_wrong_number_of_pointers() { - let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); assert!(verifier .process_movement( DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); // POINTER 1 DOWN @@ -618,8 +815,10 @@ mod tests { Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + MotionButton::empty(), &two_pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_ok()); // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected @@ -628,8 +827,487 @@ mod tests { DeviceId(1), Source::Touchscreen, input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn correct_button_press() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + } + + #[test] + fn button_press_without_action_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn button_press_with_multiple_action_buttons() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Back | MotionButton::Forward, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back | MotionButton::Forward, + ) + .is_err()); + } + + #[test] + fn button_press_without_action_button_in_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn button_release_with_action_button_in_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_err()); + } + + #[test] + fn nonbutton_action_with_action_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn nonbutton_action_with_action_button_and_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_err()); + } + + #[test] + fn nonbutton_action_with_button_state_change() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back, + ) + .is_err()); + } + + #[test] + fn nonbutton_action_missing_button_state() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Back, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn up_without_button_release() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + // This UP event shouldn't change the button state; a BUTTON_RELEASE before it should. + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_UP, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn button_press_for_already_pressed_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Back, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Back, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back, + ) + .is_err()); + } + + #[test] + fn button_release_for_unpressed_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE, + MotionButton::Back, + &pointer_properties, + MotionFlags::empty(), + MotionButton::empty(), + ) + .is_err()); + } + + #[test] + fn correct_multiple_button_presses_without_down() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Back, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Forward, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back | MotionButton::Forward, + ) + .is_ok()); + } + + #[test] + fn correct_down_with_button_press() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary | MotionButton::Secondary, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Secondary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary | MotionButton::Secondary, + ) + .is_ok()); + // Also check that the MOVE afterwards is OK, as that's where errors would be raised if not + // enough BUTTON_PRESSes were sent. + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary | MotionButton::Secondary, + ) + .is_ok()); + } + + #[test] + fn down_with_button_state_change_not_followed_by_button_press() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + // The DOWN event itself is OK, but it needs to be immediately followed by a BUTTON_PRESS. + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_err()); + } + + #[test] + fn down_with_button_state_change_not_followed_by_enough_button_presses() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary | MotionButton::Secondary, + ) + .is_ok()); + // The DOWN event itself is OK, but it needs to be immediately followed by two + // BUTTON_PRESSes, one for each button. + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Primary, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_MOVE, + MotionButton::empty(), + &pointer_properties, + MotionFlags::empty(), + MotionButton::Primary, + ) + .is_err()); + } + + #[test] + fn down_missing_already_pressed_button() { + let mut verifier = + InputVerifier::new("Test", /*should_log*/ false, /*verify_buttons*/ true); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS, + MotionButton::Back, + &pointer_properties, + MotionFlags::empty(), + MotionButton::Back, + ) + .is_ok()); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Mouse, + input_bindgen::AMOTION_EVENT_ACTION_DOWN, + MotionButton::empty(), &pointer_properties, MotionFlags::empty(), + MotionButton::empty(), ) .is_err()); } diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 4f4ea8568b..6db4356da1 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -24,8 +24,8 @@ mod keyboard_classifier; pub use data_store::{DataStore, DefaultFileReaderWriter}; pub use input::{ - DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionFlags, - Source, + DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionButton, + MotionFlags, Source, }; pub use input_verifier::InputVerifier; pub use keyboard_classifier::KeyboardClassifier; @@ -57,14 +57,17 @@ mod ffi { /// ``` type InputVerifier; #[cxx_name = create] - fn create_input_verifier(name: String) -> Box<InputVerifier>; + fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier>; + #[allow(clippy::too_many_arguments)] fn process_movement( verifier: &mut InputVerifier, device_id: i32, source: u32, action: u32, + action_button: u32, pointer_properties: &[RustPointerProperties], flags: u32, + button_state: u32, ) -> String; fn reset_device(verifier: &mut InputVerifier, device_id: i32); } @@ -115,17 +118,20 @@ mod ffi { use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties}; -fn create_input_verifier(name: String) -> Box<InputVerifier> { - Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"))) +fn create_input_verifier(name: String, verify_buttons: bool) -> Box<InputVerifier> { + Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents"), verify_buttons)) } +#[allow(clippy::too_many_arguments)] fn process_movement( verifier: &mut InputVerifier, device_id: i32, source: u32, action: u32, + action_button: u32, pointer_properties: &[RustPointerProperties], flags: u32, + button_state: u32, ) -> String { let motion_flags = MotionFlags::from_bits(flags); if motion_flags.is_none() { @@ -135,12 +141,28 @@ fn process_movement( flags ); } + let motion_action_button = MotionButton::from_bits(action_button); + if motion_action_button.is_none() { + panic!( + "The conversion of action button 0x{action_button:08x} failed, please check if some \ + buttons need to be added to MotionButton." + ); + } + let motion_button_state = MotionButton::from_bits(button_state); + if motion_button_state.is_none() { + panic!( + "The conversion of button state 0x{button_state:08x} failed, please check if some \ + buttons need to be added to MotionButton." + ); + } let result = verifier.process_movement( DeviceId(device_id), Source::from_bits(source).unwrap(), action, + motion_action_button.unwrap(), pointer_properties, motion_flags.unwrap(), + motion_button_state.unwrap(), ); match result { Ok(()) => "".to_string(), diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp index 5bb1d56040..8e0d9068c1 100644 --- a/libs/input/tests/InputVerifier_test.cpp +++ b/libs/input/tests/InputVerifier_test.cpp @@ -49,9 +49,9 @@ TEST(InputVerifierTest, ProcessSourceClassPointer) { const Result<void> result = verifier.processMovement(/*deviceId=*/0, AINPUT_SOURCE_CLASS_POINTER, - AMOTION_EVENT_ACTION_DOWN, + AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0, /*pointerCount=*/properties.size(), properties.data(), - coords.data(), /*flags=*/0); + coords.data(), /*flags=*/0, /*buttonState=*/0); ASSERT_RESULT_OK(result); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 11ba592b57..c1ddb6a2dc 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4550,8 +4550,9 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { args.displayId.toString().c_str())); Result<void> result = it->second.processMovement(args.deviceId, args.source, args.action, - args.getPointerCount(), args.pointerProperties.data(), - args.pointerCoords.data(), args.flags); + args.actionButton, args.getPointerCount(), + args.pointerProperties.data(), args.pointerCoords.data(), + args.flags, args.buttonState); if (!result.ok()) { LOG(FATAL) << "Bad stream: " << result.error() << " caused by " << args.dump(); } @@ -4747,9 +4748,10 @@ bool InputDispatcher::shouldRejectInjectedMotionLocked(const MotionEvent& motion Result<void> result = verifier.processMovement(deviceId, motionEvent.getSource(), motionEvent.getAction(), - motionEvent.getPointerCount(), + motionEvent.getActionButton(), motionEvent.getPointerCount(), motionEvent.getPointerProperties(), - motionEvent.getSamplePointerCoords(), flags); + motionEvent.getSamplePointerCoords(), flags, + motionEvent.getButtonState()); if (!result.ok()) { logDispatchStateLocked(); LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error(); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 301632ff7a..f2b2b6f2a8 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -115,6 +115,7 @@ private: ui::Rotation mOrientation{ui::ROTATION_0}; FloatRect mBoundsInLogicalDisplay{}; + // The button state as of the last sync. int32_t mButtonState; nsecs_t mDownTime; nsecs_t mLastEventTime; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index fd9884b234..914f5abdbd 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -1721,15 +1721,16 @@ protected: mParamContinueGesture(std::get<1>(GetParam())), mParamEndGesture(std::get<2>(GetParam())), mDeviceContext(*mDevice, EVENTHUB_ID), - mConverter(*mReader->getContext(), mDeviceContext, DEVICE_ID), - mVerifier("Test verifier") { + mConverter(*mReader->getContext(), mDeviceContext, DEVICE_ID) { mConverter.setDisplayId(ui::LogicalDisplayId::DEFAULT); + input_flags::enable_button_state_verification(true); + mVerifier = std::make_unique<InputVerifier>("Test verifier"); } base::Result<void> processMotionArgs(NotifyMotionArgs arg) { - return mVerifier.processMovement(arg.deviceId, arg.source, arg.action, - arg.getPointerCount(), arg.pointerProperties.data(), - arg.pointerCoords.data(), arg.flags); + return mVerifier->processMovement(arg.deviceId, arg.source, arg.action, arg.actionButton, + arg.getPointerCount(), arg.pointerProperties.data(), + arg.pointerCoords.data(), arg.flags, arg.buttonState); } void verifyArgsFromGesture(const Gesture& gesture, size_t gestureIndex) { @@ -1755,7 +1756,7 @@ protected: InputDeviceContext mDeviceContext; GestureConverter mConverter; - InputVerifier mVerifier; + std::unique_ptr<InputVerifier> mVerifier; }; TEST_P(GestureConverterConsistencyTest, ButtonChangesDuringGesture) { diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c0e2060bf8..7cc4ff76b8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -12405,43 +12405,69 @@ protected: } void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { + bool consumeButtonPress = false; switch (fromSource) { - case AINPUT_SOURCE_TOUCHSCREEN: + case AINPUT_SOURCE_TOUCHSCREEN: { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, {50, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; break; - case AINPUT_SOURCE_STYLUS: + } + case AINPUT_SOURCE_STYLUS: { + PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS) .buttonState( AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) - .pointer(PointerBuilder(0, ToolType::STYLUS) - .x(50) - .y(50)) + .pointer(pointer) + .build())); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, + AINPUT_SOURCE_STYLUS) + .actionButton( + AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .buttonState( + AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .pointer(pointer) .build())); + consumeButtonPress = true; break; - case AINPUT_SOURCE_MOUSE: + } + case AINPUT_SOURCE_MOUSE: { + PointerBuilder pointer = + PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) - .pointer(PointerBuilder(MOUSE_POINTER_ID, - ToolType::MOUSE) - .x(50) - .y(50)) + .pointer(pointer) .build())); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, + AINPUT_SOURCE_MOUSE) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(pointer) + .build())); + consumeButtonPress = true; break; - default: + } + default: { FAIL() << "Source " << fromSource << " doesn't support drag and drop"; + } } // Window should receive motion event. mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); + if (consumeButtonPress) { + mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + } // Spy window should also receive motion event mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } @@ -12641,6 +12667,16 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { // Move to another window and release button, expect to drop item. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, + AINPUT_SOURCE_STYLUS) + .actionButton(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .buttonState(0) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS) .buttonState(0) .pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50)) @@ -12882,6 +12918,18 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) { // drop to another window. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, + AINPUT_SOURCE_MOUSE) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .buttonState(0) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) .buttonState(0) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE) diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp index 31db2fedc7..abce931eff 100644 --- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp @@ -48,9 +48,9 @@ public: auto [it, _] = mVerifiers.emplace(args.displayId, "Fuzz Verifier"); InputVerifier& verifier = it->second; const Result<void> result = - verifier.processMovement(args.deviceId, args.source, args.action, + verifier.processMovement(args.deviceId, args.source, args.action, args.actionButton, args.getPointerCount(), args.pointerProperties.data(), - args.pointerCoords.data(), args.flags); + args.pointerCoords.data(), args.flags, args.buttonState); if (result.ok()) { return args; } |