Add hovering support to verifier

In order to allow fuzzing of dispatcher, we need to be able to avoid
incorrect hover sequences sent to the listener.

Add hovering support for verifier in this CL.

Bug: 211379801
Test: TEST=inputflinger_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST
Change-Id: If7ee8ecb62044768915acc4657029366e193c6db
diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h
index 3715408..b857482 100644
--- a/include/input/InputVerifier.h
+++ b/include/input/InputVerifier.h
@@ -51,6 +51,8 @@
                                                 const PointerProperties* pointerProperties,
                                                 const PointerCoords* pointerCoords, int32_t flags);
 
+    void resetDevice(int32_t deviceId);
+
 private:
     rust::Box<android::input::verifier::InputVerifier> mVerifier;
 };
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index b0546a5..851babf 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -24,6 +24,8 @@
 using android::base::Result;
 using android::input::RustPointerProperties;
 
+using DeviceId = int32_t;
+
 namespace android {
 
 // --- InputVerifier ---
@@ -31,7 +33,8 @@
 InputVerifier::InputVerifier(const std::string& name)
       : mVerifier(android::input::verifier::create(name)){};
 
-Result<void> InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount,
+Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
+                                            uint32_t pointerCount,
                                             const PointerProperties* pointerProperties,
                                             const PointerCoords* pointerCoords, int32_t flags) {
     std::vector<RustPointerProperties> rpp;
@@ -49,4 +52,8 @@
     }
 }
 
+void InputVerifier::resetDevice(DeviceId deviceId) {
+    android::input::verifier::reset_device(*mVerifier, deviceId);
+}
+
 } // namespace android
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 1cc1129..fdb6230 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -27,6 +27,7 @@
     name: String,
     should_log: bool,
     touching_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
+    hovering_pointer_ids_by_device: HashMap<DeviceId, HashSet<i32>>,
 }
 
 impl InputVerifier {
@@ -37,7 +38,12 @@
                 .with_tag_on_device("InputVerifier")
                 .with_min_level(log::Level::Trace),
         );
-        Self { name: name.to_owned(), should_log, touching_pointer_ids_by_device: HashMap::new() }
+        Self {
+            name: name.to_owned(),
+            should_log,
+            touching_pointer_ids_by_device: HashMap::new(),
+            hovering_pointer_ids_by_device: HashMap::new(),
+        }
     }
 
     /// Process a pointer movement event from an InputDevice.
@@ -135,7 +141,7 @@
                         self.name, pointer_id, it, device_id
                     ));
                 }
-                it.clear();
+                self.touching_pointer_ids_by_device.remove(&device_id);
             }
             MotionAction::Cancel => {
                 if flags.contains(MotionFlags::CANCELED) {
@@ -153,11 +159,68 @@
                 }
                 self.touching_pointer_ids_by_device.remove(&device_id);
             }
+            /*
+             * The hovering protocol currently supports a single pointer only, because we do not
+             * have ACTION_HOVER_POINTER_ENTER or ACTION_HOVER_POINTER_EXIT.
+             * Still, we are keeping the infrastructure here pretty general in case that is
+             * eventually supported.
+             */
+            MotionAction::HoverEnter => {
+                if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
+                        {:?}",
+                        self.name, device_id, self.hovering_pointer_ids_by_device
+                    ));
+                }
+                let it = self
+                    .hovering_pointer_ids_by_device
+                    .entry(device_id)
+                    .or_insert_with(HashSet::new);
+                it.insert(pointer_properties[0].id);
+            }
+            MotionAction::HoverMove => {
+                // For compatibility reasons, we allow HOVER_MOVE without a prior HOVER_ENTER.
+                // If there was no prior HOVER_ENTER, just start a new hovering pointer.
+                let it = self
+                    .hovering_pointer_ids_by_device
+                    .entry(device_id)
+                    .or_insert_with(HashSet::new);
+                it.insert(pointer_properties[0].id);
+            }
+            MotionAction::HoverExit => {
+                if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+                    return Err(format!(
+                        "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
+                        self.name, device_id
+                    ));
+                }
+                let pointer_id = pointer_properties[0].id;
+                let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                it.remove(&pointer_id);
+
+                if !it.is_empty() {
+                    return Err(format!(
+                        "{}: Removed hovering pointer {}, but pointers are still\
+                               hovering for device {:?}: {:?}",
+                        self.name, pointer_id, device_id, it
+                    ));
+                }
+                self.hovering_pointer_ids_by_device.remove(&device_id);
+            }
             _ => return Ok(()),
         }
         Ok(())
     }
 
+    /// Notify the verifier that the device has been reset, which will cause the verifier to erase
+    /// the current internal state for this device. Subsequent events from this device are expected
+    //// to start a new gesture.
+    pub fn reset_device(&mut self, device_id: DeviceId) {
+        self.touching_pointer_ids_by_device.remove(&device_id);
+        self.hovering_pointer_ids_by_device.remove(&device_id);
+    }
+
     fn ensure_touching_pointers_match(
         &self,
         device_id: DeviceId,
@@ -272,4 +335,68 @@
             )
             .is_err());
     }
+
+    #[test]
+    fn correct_hover_sequence() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+    }
+
+    #[test]
+    fn double_hover_enter() {
+        let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+        let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_ok());
+
+        assert!(verifier
+            .process_movement(
+                DeviceId(1),
+                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+                &pointer_properties,
+                MotionFlags::empty(),
+            )
+            .is_err());
+    }
 }
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 25b2ecb..892f558 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -54,6 +54,7 @@
             pointer_properties: &[RustPointerProperties],
             flags: i32,
         ) -> String;
+        fn reset_device(verifier: &mut InputVerifier, device_id: i32);
     }
 
     #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@@ -64,6 +65,10 @@
 
 use crate::ffi::RustPointerProperties;
 
+fn create(name: String) -> Box<InputVerifier> {
+    Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
+}
+
 fn process_movement(
     verifier: &mut InputVerifier,
     device_id: i32,
@@ -83,6 +88,6 @@
     }
 }
 
-fn create(name: String) -> Box<InputVerifier> {
-    Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
+fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
+    verifier.reset_device(DeviceId(device_id));
 }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 29c4e46..482e23f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4516,6 +4516,10 @@
         std::unique_ptr<DeviceResetEntry> newEntry =
                 std::make_unique<DeviceResetEntry>(args.id, args.eventTime, args.deviceId);
         needWake = enqueueInboundEventLocked(std::move(newEntry));
+
+        for (auto& [_, verifier] : mVerifiersByDisplay) {
+            verifier.resetDevice(args.deviceId);
+        }
     } // release lock
 
     if (needWake) {