Merge "InputDispatcher: use INFO for conditional logs" into main
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index bb45ad2..993ad82 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -263,7 +263,6 @@
         "android.utils.IMemory",
         "android.utils.IMemoryHeap",
         "com.android.car.procfsinspector.IProcfsInspector",
-        "com.android.internal.app.IAppOpsCallback",
         "com.android.internal.app.IAppOpsService",
         "com.android.internal.app.IBatteryStats",
         "com.android.internal.os.IResultReceiver",
diff --git a/libs/binder/tests/binderStabilityIntegrationTest.cpp b/libs/binder/tests/binderStabilityIntegrationTest.cpp
index a3fc9cc..cbc4180 100644
--- a/libs/binder/tests/binderStabilityIntegrationTest.cpp
+++ b/libs/binder/tests/binderStabilityIntegrationTest.cpp
@@ -47,6 +47,7 @@
 
     Stability::Level level = Stability::Level::UNDECLARED;
     switch (partition) {
+        case Partition::PRODUCT:
         case Partition::SYSTEM:
         case Partition::SYSTEM_EXT:
             level = Stability::Level::SYSTEM;
diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs
index a8acded..16147dd 100644
--- a/libs/debugstore/rust/src/core.rs
+++ b/libs/debugstore/rust/src/core.rs
@@ -48,7 +48,7 @@
     ///
     /// This constant is used as a part of the debug store's data format,
     /// allowing for version tracking and compatibility checks.
-    const ENCODE_VERSION: u32 = 2;
+    const ENCODE_VERSION: u32 = 3;
 
     /// Creates a new instance of `DebugStore` with specified event limit and maximum delay.
     fn new() -> Self {
@@ -123,9 +123,11 @@
 
 impl fmt::Display for DebugStore {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Write the debug store header information
         let uptime_now = uptimeMillis();
         write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?;
 
+        // Join events with a separator
         write!(
             f,
             "{}",
@@ -136,7 +138,10 @@
                 acc.push_str(&event.to_string());
                 acc
             })
-        )
+        )?;
+
+        // Write the debug store footer
+        write!(f, ";;")
     }
 }
 
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 6eb2d73..35ba04f 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -50,7 +50,7 @@
 
 bitflags! {
     /// Source of the input device or input events.
-    #[derive(Debug, PartialEq)]
+    #[derive(Clone, Copy, Debug, PartialEq)]
     pub struct Source: u32 {
         // Constants from SourceClass, added here for compatibility reasons
         /// SourceClass::Button
@@ -101,7 +101,7 @@
 
 /// A rust enum representation of a MotionEvent action.
 #[repr(u32)]
-#[derive(Eq, PartialEq)]
+#[derive(Clone, Copy, Eq, PartialEq)]
 pub enum MotionAction {
     /// ACTION_DOWN
     Down = input_bindgen::AMOTION_EVENT_ACTION_DOWN,
@@ -132,9 +132,15 @@
     /// ACTION_SCROLL
     Scroll = input_bindgen::AMOTION_EVENT_ACTION_SCROLL,
     /// ACTION_BUTTON_PRESS
-    ButtonPress = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
+    ButtonPress {
+        /// The button being pressed.
+        action_button: MotionButton,
+    } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS,
     /// ACTION_BUTTON_RELEASE
-    ButtonRelease = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+    ButtonRelease {
+        /// The button being released.
+        action_button: MotionButton,
+    } = input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE,
 }
 
 impl fmt::Display for MotionAction {
@@ -153,14 +159,20 @@
             MotionAction::Scroll => write!(f, "SCROLL"),
             MotionAction::HoverEnter => write!(f, "HOVER_ENTER"),
             MotionAction::HoverExit => write!(f, "HOVER_EXIT"),
-            MotionAction::ButtonPress => write!(f, "BUTTON_PRESS"),
-            MotionAction::ButtonRelease => write!(f, "BUTTON_RELEASE"),
+            MotionAction::ButtonPress { action_button } => {
+                write!(f, "BUTTON_PRESS({action_button:?})")
+            }
+            MotionAction::ButtonRelease { action_button } => {
+                write!(f, "BUTTON_RELEASE({action_button:?})")
+            }
         }
     }
 }
 
-impl From<u32> for MotionAction {
-    fn from(action: u32) -> Self {
+impl MotionAction {
+    /// Creates a [`MotionAction`] from an `AMOTION_EVENT_ACTION_…` constant and an action button
+    /// (which should be empty for all actions except `BUTTON_PRESS` and `…_RELEASE`).
+    pub fn from_code(action: u32, action_button: MotionButton) -> Self {
         let (action_masked, action_index) = MotionAction::breakdown_action(action);
         match action_masked {
             input_bindgen::AMOTION_EVENT_ACTION_DOWN => MotionAction::Down,
@@ -178,14 +190,16 @@
             input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE => MotionAction::HoverMove,
             input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT => MotionAction::HoverExit,
             input_bindgen::AMOTION_EVENT_ACTION_SCROLL => MotionAction::Scroll,
-            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => MotionAction::ButtonPress,
-            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => MotionAction::ButtonRelease,
+            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_PRESS => {
+                MotionAction::ButtonPress { action_button }
+            }
+            input_bindgen::AMOTION_EVENT_ACTION_BUTTON_RELEASE => {
+                MotionAction::ButtonRelease { action_button }
+            }
             _ => panic!("Unknown action: {}", action),
         }
     }
-}
 
-impl MotionAction {
     fn breakdown_action(action: u32) -> (u32, usize) {
         let action_masked = action & input_bindgen::AMOTION_EVENT_ACTION_MASK;
         let index = (action & input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
@@ -219,7 +233,7 @@
     /// 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.
-    #[derive(Debug)]
+    #[derive(Clone, Copy, Debug)]
     pub struct MotionFlags: u32 {
         /// FLAG_WINDOW_IS_OBSCURED
         const WINDOW_IS_OBSCURED = MotionEventFlag::WINDOW_IS_OBSCURED.0 as u32;
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index 6d94316..f87dd41 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -22,26 +22,50 @@
 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();
+/// Represents a movement or state change event from a pointer device. The Rust equivalent of the
+/// C++ NotifyMotionArgs struct.
+#[derive(Clone, Copy)]
+pub struct NotifyMotionArgs<'a> {
+    /// The ID of the device that emitted the event.
+    pub device_id: DeviceId,
+
+    /// The type of device that emitted the event.
+    pub source: Source,
+
+    /// The type of event that took place.
+    pub action: MotionAction,
+
+    /// The properties of each of the pointers involved in the event.
+    pub pointer_properties: &'a [RustPointerProperties],
+
+    /// Flags applied to the event.
+    pub flags: MotionFlags,
+
+    /// The set of buttons that were pressed at the time of the event.
+    ///
+    /// 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` |
+    pub button_state: MotionButton,
+}
+
+/// Verifies the properties of an event that should always be true, regardless of the current state.
+fn verify_event(event: NotifyMotionArgs<'_>, verify_buttons: bool) -> Result<(), String> {
+    let pointer_count = event.pointer_properties.len();
     if pointer_count < 1 {
-        return Err(format!("Invalid {} event: no pointers", action));
+        return Err(format!("Invalid {} event: no pointers", event.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 {
+    match event.action {
         MotionAction::Down
         | MotionAction::HoverEnter
         | MotionAction::HoverExit
@@ -49,32 +73,39 @@
         | MotionAction::Up => {
             if pointer_count != 1 {
                 return Err(format!(
-                    "Invalid {action} event: there are {pointer_count} pointers in the event",
+                    "Invalid {} event: there are {} pointers in the event",
+                    event.action, pointer_count
                 ));
             }
         }
 
         MotionAction::Cancel => {
-            if !flags.contains(MotionFlags::CANCELED) {
+            if !event.flags.contains(MotionFlags::CANCELED) {
                 return Err(format!(
-                    "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {flags:#?}",
+                    "For ACTION_CANCEL, must set FLAG_CANCELED. Received flags: {:#?}",
+                    event.flags
                 ));
             }
         }
 
         MotionAction::PointerDown { action_index } | MotionAction::PointerUp { action_index } => {
             if action_index >= pointer_count {
-                return Err(format!("Got {action}, but event has {pointer_count} pointer(s)"));
+                return Err(format!(
+                    "Got {}, but event has {} pointer(s)",
+                    event.action, pointer_count
+                ));
             }
         }
 
-        MotionAction::ButtonPress | MotionAction::ButtonRelease => {
+        MotionAction::ButtonPress { action_button }
+        | MotionAction::ButtonRelease { action_button } => {
             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"
+                        "Invalid {} event: must specify a single action button, not {} action \
+                         buttons",
+                        event.action, button_count
                     ));
                 }
             }
@@ -92,59 +123,43 @@
     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` |
+    /// for which we haven't seen BUTTON_PRESS events yet (see [`NotifyMotionArgs::button_state`]).
     pending_buttons: MotionButton,
 }
 
 impl ButtonVerifier {
-    pub fn process_action(
-        &mut self,
-        action: MotionAction,
-        action_button: MotionButton,
-        button_state: MotionButton,
-    ) -> Result<(), String> {
+    pub fn process_event(&mut self, event: NotifyMotionArgs<'_>) -> 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() {
+            match event.action {
+                MotionAction::ButtonPress { action_button }
+                    if self.pending_buttons.contains(action_button) =>
+                {
+                    self.pending_buttons -= action_button;
+                }
+                _ => {
                     return Err(format!(
-                        "DOWN event button state is missing {:?}",
-                        self.button_state - button_state
+                        "After DOWN event, expected BUTTON_PRESS event(s) for {:?}, but got {}",
+                        self.pending_buttons, event.action
                     ));
                 }
-                self.pending_buttons = button_state - self.button_state;
+            }
+        }
+        let expected_state = match event.action {
+            MotionAction::Down => {
+                if self.button_state - event.button_state != MotionButton::empty() {
+                    return Err(format!(
+                        "DOWN event button state is missing {:?}",
+                        self.button_state - event.button_state
+                    ));
+                }
+                self.pending_buttons = event.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
+                event.button_state
             }
-            MotionAction::ButtonPress => {
+            MotionAction::ButtonPress { action_button } => {
                 if self.button_state.contains(action_button) {
                     return Err(format!(
                         "Duplicate BUTTON_PRESS; button state already contains {action_button:?}"
@@ -152,24 +167,25 @@
                 }
                 self.button_state | action_button
             }
-            MotionAction::ButtonRelease => {
+            MotionAction::ButtonRelease { action_button } => {
                 if !self.button_state.contains(action_button) {
                     return Err(format!(
-                        "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}"
+                        "Invalid BUTTON_RELEASE; button state doesn't contain {action_button:?}",
                     ));
                 }
                 self.button_state - action_button
             }
             _ => self.button_state,
         };
-        if button_state != expected_state {
+        if event.button_state != expected_state {
             return Err(format!(
-                "Expected {action} button state to be {expected_state:?}, but was {button_state:?}"
+                "Expected {} button state to be {:?}, but was {:?}",
+                event.action, expected_state, event.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;
+        if event.action != MotionAction::Down {
+            self.button_state = event.button_state;
         }
         Ok(())
     }
@@ -205,78 +221,61 @@
 
     /// 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) {
+    pub fn process_movement(&mut self, event: NotifyMotionArgs<'_>) -> Result<(), String> {
+        if !event.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 {}",
-                MotionAction::from(action).to_string(),
-                device_id,
-                pointer_properties.len(),
-                if pointer_properties.len() == 1 { "" } else { "s" },
+                event.action,
+                event.device_id,
+                event.pointer_properties.len(),
+                if event.pointer_properties.len() == 1 { "" } else { "s" },
                 self.name
             );
         }
 
-        verify_event(
-            action.into(),
-            action_button,
-            pointer_properties,
-            &flags,
-            self.verify_buttons,
-        )?;
+        verify_event(event, 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,
-            )?;
+            self.button_verifier_by_device
+                .entry(event.device_id)
+                .or_default()
+                .process_event(event)?;
         }
 
-        match action.into() {
+        match event.action {
             MotionAction::Down => {
-                if self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                if self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
                     return Err(format!(
                         "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}",
-                        self.name, device_id, self.touching_pointer_ids_by_device
+                        self.name, event.device_id, self.touching_pointer_ids_by_device
                     ));
                 }
-                let it = self.touching_pointer_ids_by_device.entry(device_id).or_default();
-                it.insert(pointer_properties[0].id);
+                let it = self.touching_pointer_ids_by_device.entry(event.device_id).or_default();
+                it.insert(event.pointer_properties[0].id);
             }
             MotionAction::PointerDown { action_index } => {
-                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                if !self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
                     return Err(format!(
                         "{}: Received POINTER_DOWN but no pointers are currently down \
                         for device {:?}",
-                        self.name, device_id
+                        self.name, event.device_id
                     ));
                 }
-                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
-                if it.len() != pointer_properties.len() - 1 {
+                let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
+                if it.len() != event.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()
+                        event.pointer_properties.len()
                     ));
                 }
-                let pointer_id = pointer_properties[action_index].id;
+                let pointer_id = event.pointer_properties[action_index].id;
                 if it.contains(&pointer_id) {
                     return Err(format!(
                         "{}: Pointer with id={} already present found in the properties",
@@ -286,7 +285,7 @@
                 it.insert(pointer_id);
             }
             MotionAction::Move => {
-                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+                if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) {
                     return Err(format!(
                         "{}: ACTION_MOVE touching pointers don't match",
                         self.name
@@ -294,49 +293,49 @@
                 }
             }
             MotionAction::PointerUp { action_index } => {
-                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+                if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) {
                     return Err(format!(
                         "{}: ACTION_POINTER_UP touching pointers don't match",
                         self.name
                     ));
                 }
-                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
-                let pointer_id = pointer_properties[action_index].id;
+                let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
+                let pointer_id = event.pointer_properties[action_index].id;
                 it.remove(&pointer_id);
             }
             MotionAction::Up => {
-                if !self.touching_pointer_ids_by_device.contains_key(&device_id) {
+                if !self.touching_pointer_ids_by_device.contains_key(&event.device_id) {
                     return Err(format!(
                         "{} Received ACTION_UP but no pointers are currently down for device {:?}",
-                        self.name, device_id
+                        self.name, event.device_id
                     ));
                 }
-                let it = self.touching_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                let it = self.touching_pointer_ids_by_device.get_mut(&event.device_id).unwrap();
                 if it.len() != 1 {
                     return Err(format!(
                         "{}: Got ACTION_UP, but we have pointers: {:?} for device {:?}",
-                        self.name, it, device_id
+                        self.name, it, event.device_id
                     ));
                 }
-                let pointer_id = pointer_properties[0].id;
+                let pointer_id = event.pointer_properties[0].id;
                 if !it.contains(&pointer_id) {
                     return Err(format!(
                         "{}: Got ACTION_UP, but pointerId {} is not touching. Touching pointers:\
                         {:?} for device {:?}",
-                        self.name, pointer_id, it, device_id
+                        self.name, pointer_id, it, event.device_id
                     ));
                 }
-                self.touching_pointer_ids_by_device.remove(&device_id);
+                self.touching_pointer_ids_by_device.remove(&event.device_id);
             }
             MotionAction::Cancel => {
-                if !self.ensure_touching_pointers_match(device_id, pointer_properties) {
+                if !self.ensure_touching_pointers_match(event.device_id, event.pointer_properties) {
                     return Err(format!(
                         "{}: Got ACTION_CANCEL, but the pointers don't match. \
                         Existing pointers: {:?}",
                         self.name, self.touching_pointer_ids_by_device
                     ));
                 }
-                self.touching_pointer_ids_by_device.remove(&device_id);
+                self.touching_pointer_ids_by_device.remove(&event.device_id);
             }
             /*
              * The hovering protocol currently supports a single pointer only, because we do not
@@ -345,41 +344,41 @@
              * eventually supported.
              */
             MotionAction::HoverEnter => {
-                if self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+                if self.hovering_pointer_ids_by_device.contains_key(&event.device_id) {
                     return Err(format!(
                         "{}: Invalid HOVER_ENTER event - pointers already hovering for device {:?}:\
                         {:?}",
-                        self.name, device_id, self.hovering_pointer_ids_by_device
+                        self.name, event.device_id, self.hovering_pointer_ids_by_device
                     ));
                 }
-                let it = self.hovering_pointer_ids_by_device.entry(device_id).or_default();
-                it.insert(pointer_properties[0].id);
+                let it = self.hovering_pointer_ids_by_device.entry(event.device_id).or_default();
+                it.insert(event.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_default();
-                it.insert(pointer_properties[0].id);
+                let it = self.hovering_pointer_ids_by_device.entry(event.device_id).or_default();
+                it.insert(event.pointer_properties[0].id);
             }
             MotionAction::HoverExit => {
-                if !self.hovering_pointer_ids_by_device.contains_key(&device_id) {
+                if !self.hovering_pointer_ids_by_device.contains_key(&event.device_id) {
                     return Err(format!(
                         "{}: Invalid HOVER_EXIT event - no pointers are hovering for device {:?}",
-                        self.name, device_id
+                        self.name, event.device_id
                     ));
                 }
-                let pointer_id = pointer_properties[0].id;
-                let it = self.hovering_pointer_ids_by_device.get_mut(&device_id).unwrap();
+                let pointer_id = event.pointer_properties[0].id;
+                let it = self.hovering_pointer_ids_by_device.get_mut(&event.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.name, pointer_id, event.device_id, it
                     ));
                 }
-                self.hovering_pointer_ids_by_device.remove(&device_id);
+                self.hovering_pointer_ids_by_device.remove(&event.device_id);
             }
             _ => return Ok(()),
         }
@@ -421,11 +420,25 @@
 mod tests {
     use crate::input::MotionButton;
     use crate::input_verifier::InputVerifier;
+    use crate::input_verifier::NotifyMotionArgs;
     use crate::DeviceId;
+    use crate::MotionAction;
     use crate::MotionFlags;
     use crate::RustPointerProperties;
     use crate::Source;
 
+    const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }];
+    const BASE_EVENT: NotifyMotionArgs = NotifyMotionArgs {
+        device_id: DeviceId(1),
+        source: Source::Touchscreen,
+        action: MotionAction::Down,
+        pointer_properties: &BASE_POINTER_PROPERTIES,
+        flags: MotionFlags::empty(),
+        button_state: MotionButton::empty(),
+    };
+    const BASE_MOUSE_EVENT: NotifyMotionArgs =
+        NotifyMotionArgs { source: Source::Mouse, ..BASE_EVENT };
+
     #[test]
     /**
      * Send a DOWN event with 2 pointers and ensure that it's marked as invalid.
@@ -436,15 +449,11 @@
         let pointer_properties =
             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_err());
     }
 
@@ -454,37 +463,25 @@
             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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Move,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_UP,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Up,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
     }
 
@@ -494,56 +491,38 @@
             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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         // POINTER 1 DOWN
         let two_pointer_properties =
             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::PointerDown { action_index: 1 },
+                pointer_properties: &two_pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         // POINTER 0 UP
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::PointerUp { action_index: 0 },
+                pointer_properties: &two_pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         // ACTION_UP for pointer id=1
         let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]);
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_UP,
-                MotionButton::empty(),
-                &pointer_1_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Up,
+                pointer_properties: &pointer_1_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
     }
 
@@ -551,61 +530,40 @@
     fn multi_device_stream() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                device_id: DeviceId(1),
+                action: MotionAction::Down,
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                device_id: DeviceId(1),
+                action: MotionAction::Move,
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(2),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                device_id: DeviceId(2),
+                action: MotionAction::Down,
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(2),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                device_id: DeviceId(2),
+                action: MotionAction::Move,
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_UP,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                device_id: DeviceId(1),
+                action: MotionAction::Up,
+                ..BASE_EVENT
+            })
             .is_ok());
     }
 
@@ -613,28 +571,19 @@
     fn action_cancel() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                flags: MotionFlags::empty(),
+                ..BASE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::CANCELED,
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Cancel,
+                flags: MotionFlags::CANCELED,
+                ..BASE_EVENT
+            })
             .is_ok());
     }
 
@@ -642,28 +591,11 @@
     fn invalid_action_cancel() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::Down, ..BASE_EVENT })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_CANCEL,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(), // forgot to set FLAG_CANCELED
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::Cancel, ..BASE_EVENT })
             .is_err());
     }
 
@@ -671,17 +603,8 @@
     fn invalid_up() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::Up, ..BASE_EVENT })
             .is_err());
     }
 
@@ -689,53 +612,20 @@
     fn correct_hover_sequence() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_HOVER_MOVE,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverMove, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_HOVER_EXIT,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverExit, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_ok());
     }
 
@@ -743,29 +633,12 @@
     fn double_hover_enter() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_ok());
 
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs { action: MotionAction::HoverEnter, ..BASE_EVENT })
             .is_err());
     }
 
@@ -775,17 +648,13 @@
     fn relative_mouse_move() {
         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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                device_id: DeviceId(2),
+                source: Source::MouseRelative,
+                action: MotionAction::Move,
+                ..BASE_EVENT
+            })
             .is_ok());
     }
 
@@ -796,42 +665,29 @@
             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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         // POINTER 1 DOWN
         let two_pointer_properties =
             Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::PointerDown { action_index: 1 },
+                pointer_properties: &two_pointer_properties,
+                ..BASE_EVENT
+            })
             .is_ok());
         // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Touchscreen,
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Move,
+                pointer_properties: &pointer_properties,
+                ..BASE_EVENT
+            })
             .is_err());
     }
 
@@ -839,17 +695,12 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .is_ok());
     }
 
@@ -857,17 +708,12 @@
     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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::empty() },
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -875,17 +721,14 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress {
+                    action_button: MotionButton::Back | MotionButton::Forward
+                },
+                button_state: MotionButton::Back | MotionButton::Forward,
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -893,17 +736,12 @@
     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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -911,64 +749,19 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonRelease { action_button: MotionButton::Primary },
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -976,28 +769,19 @@
     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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::HoverEnter,
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::HoverMove,
+                button_state: MotionButton::Back,
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1005,39 +789,26 @@
     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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::HoverEnter,
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+                button_state: MotionButton::Back,
+                ..BASE_MOUSE_EVENT
+            })
             .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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::HoverMove,
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1045,40 +816,27 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Up,
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1086,28 +844,19 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+                button_state: MotionButton::Back,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+                button_state: MotionButton::Back,
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1115,17 +864,12 @@
     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(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonRelease { action_button: MotionButton::Back },
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1133,28 +877,19 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+                button_state: MotionButton::Back,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Forward },
+                button_state: MotionButton::Back | MotionButton::Forward,
+                ..BASE_MOUSE_EVENT
+            })
             .is_ok());
     }
 
@@ -1162,52 +897,35 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                button_state: MotionButton::Primary | MotionButton::Secondary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Secondary },
+                button_state: MotionButton::Primary | MotionButton::Secondary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Move,
+                button_state: MotionButton::Primary | MotionButton::Secondary,
+                ..BASE_MOUSE_EVENT
+            })
             .is_ok());
     }
 
@@ -1215,29 +933,20 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Move,
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1245,41 +954,28 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                button_state: MotionButton::Primary | MotionButton::Secondary,
+                ..BASE_MOUSE_EVENT
+            })
             .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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Primary },
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Mouse,
-                input_bindgen::AMOTION_EVENT_ACTION_MOVE,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::Primary,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Move,
+                button_state: MotionButton::Primary,
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 
@@ -1287,28 +983,19 @@
     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,
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::ButtonPress { action_button: MotionButton::Back },
+                button_state: MotionButton::Back,
+                ..BASE_MOUSE_EVENT
+            })
             .is_ok());
         assert!(verifier
-            .process_movement(
-                DeviceId(1),
-                Source::Mouse,
-                input_bindgen::AMOTION_EVENT_ACTION_DOWN,
-                MotionButton::empty(),
-                &pointer_properties,
-                MotionFlags::empty(),
-                MotionButton::empty(),
-            )
+            .process_movement(NotifyMotionArgs {
+                action: MotionAction::Down,
+                button_state: MotionButton::empty(),
+                ..BASE_MOUSE_EVENT
+            })
             .is_err());
     }
 }
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 6db4356..7638559 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -27,7 +27,7 @@
     DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionButton,
     MotionFlags, Source,
 };
-pub use input_verifier::InputVerifier;
+pub use input_verifier::{InputVerifier, NotifyMotionArgs};
 pub use keyboard_classifier::KeyboardClassifier;
 
 #[cxx::bridge(namespace = "android::input")]
@@ -133,37 +133,46 @@
     flags: u32,
     button_state: u32,
 ) -> String {
-    let motion_flags = MotionFlags::from_bits(flags);
-    if motion_flags.is_none() {
+    let Some(motion_flags) = MotionFlags::from_bits(flags) else {
         panic!(
             "The conversion of flags 0x{:08x} failed, please check if some flags have not been \
             added to MotionFlags.",
             flags
         );
-    }
-    let motion_action_button = MotionButton::from_bits(action_button);
-    if motion_action_button.is_none() {
+    };
+    let Some(motion_action_button) = MotionButton::from_bits(action_button) else {
         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() {
+    };
+    let Some(motion_button_state) = MotionButton::from_bits(button_state) else {
         panic!(
             "The conversion of button state 0x{button_state:08x} failed, please check if some \
              buttons need to be added to MotionButton."
         );
+    };
+    let motion_action = MotionAction::from_code(action, motion_action_button);
+    if motion_action_button != MotionButton::empty() {
+        match motion_action {
+            MotionAction::ButtonPress { action_button: _ }
+            | MotionAction::ButtonRelease { action_button: _ } => {}
+            _ => {
+                return format!(
+                    "Invalid {motion_action} event: has action button {motion_action_button:?} but \
+                     is not a button action"
+                );
+            }
+        }
     }
-    let result = verifier.process_movement(
-        DeviceId(device_id),
-        Source::from_bits(source).unwrap(),
-        action,
-        motion_action_button.unwrap(),
+    let result = verifier.process_movement(NotifyMotionArgs {
+        device_id: DeviceId(device_id),
+        source: Source::from_bits(source).unwrap(),
+        action: motion_action,
         pointer_properties,
-        motion_flags.unwrap(),
-        motion_button_state.unwrap(),
-    );
+        flags: motion_flags,
+        button_state: motion_button_state,
+    });
     match result {
         Ok(()) => "".to_string(),
         Err(e) => e,
@@ -230,3 +239,44 @@
     }
     classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap());
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::create_input_verifier;
+    use crate::process_movement;
+    use crate::RustPointerProperties;
+
+    const BASE_POINTER_PROPERTIES: [RustPointerProperties; 1] = [RustPointerProperties { id: 0 }];
+
+    #[test]
+    fn verify_nonbutton_action_with_action_button() {
+        let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
+        assert!(process_movement(
+            &mut verifier,
+            1,
+            input_bindgen::AINPUT_SOURCE_MOUSE,
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+            input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+            &BASE_POINTER_PROPERTIES,
+            0,
+            0,
+        )
+        .contains("button action"));
+    }
+
+    #[test]
+    fn verify_nonbutton_action_with_action_button_and_button_state() {
+        let mut verifier = create_input_verifier("Test".to_string(), /*verify_buttons*/ true);
+        assert!(process_movement(
+            &mut verifier,
+            1,
+            input_bindgen::AINPUT_SOURCE_MOUSE,
+            input_bindgen::AMOTION_EVENT_ACTION_HOVER_ENTER,
+            input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+            &BASE_POINTER_PROPERTIES,
+            0,
+            input_bindgen::AMOTION_EVENT_BUTTON_PRIMARY,
+        )
+        .contains("button action"));
+    }
+}
diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp
index 0eeca54..929f067 100644
--- a/libs/permission/Android.bp
+++ b/libs/permission/Android.bp
@@ -16,6 +16,7 @@
     double_loadable: true,
     srcs: [
         "aidl/android/content/AttributionSourceState.aidl",
+        "aidl/com/android/internal/app/IAppOpsCallback.aidl",
         "aidl/android/permission/IPermissionChecker.aidl",
     ],
 }
@@ -36,7 +37,6 @@
     ],
     srcs: [
         "AppOpsManager.cpp",
-        "IAppOpsCallback.cpp",
         "IAppOpsService.cpp",
         "android/permission/PermissionChecker.cpp",
     ],
diff --git a/libs/permission/IAppOpsCallback.cpp b/libs/permission/IAppOpsCallback.cpp
deleted file mode 100644
index 2b3f462..0000000
--- a/libs/permission/IAppOpsCallback.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AppOpsCallback"
-
-#include <binder/IAppOpsCallback.h>
-
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-#include <utils/String8.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpAppOpsCallback : public BpInterface<IAppOpsCallback>
-{
-public:
-    explicit BpAppOpsCallback(const sp<IBinder>& impl)
-        : BpInterface<IAppOpsCallback>(impl)
-    {
-    }
-
-    virtual void opChanged(int32_t op, const String16& packageName) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor());
-        data.writeInt32(op);
-        data.writeString16(packageName);
-        remote()->transact(OP_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
-    }
-};
-
-IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback")
-
-// ----------------------------------------------------------------------
-
-// NOLINTNEXTLINE(google-default-arguments)
-status_t BnAppOpsCallback::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
-    switch(code) {
-        case OP_CHANGED_TRANSACTION: {
-            CHECK_INTERFACE(IAppOpsCallback, data, reply);
-            int32_t op = data.readInt32();
-            String16 packageName;
-            (void)data.readString16(&packageName);
-            opChanged(op, packageName);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-} // namespace android
diff --git a/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl b/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl
new file mode 100644
index 0000000..36b19df
--- /dev/null
+++ b/libs/permission/aidl/com/android/internal/app/IAppOpsCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+oneway interface IAppOpsCallback {
+    void opChanged(int op, int uid, String packageName, String persistentDeviceId);
+}
diff --git a/libs/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h
index 7e179d6..a22c975 100644
--- a/libs/permission/include/binder/AppOpsManager.h
+++ b/libs/permission/include/binder/AppOpsManager.h
@@ -180,10 +180,10 @@
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
             const std::optional<String16>& attributionTag);
     void startWatchingMode(int32_t op, const String16& packageName,
-            const sp<IAppOpsCallback>& callback);
+            const sp<com::android::internal::app::IAppOpsCallback>& callback);
     void startWatchingMode(int32_t op, const String16& packageName, int32_t flags,
-            const sp<IAppOpsCallback>& callback);
-    void stopWatchingMode(const sp<IAppOpsCallback>& callback);
+            const sp<com::android::internal::app::IAppOpsCallback>& callback);
+    void stopWatchingMode(const sp<com::android::internal::app::IAppOpsCallback>& callback);
     int32_t permissionToOpCode(const String16& permission);
     void setCameraAudioRestriction(int32_t mode);
 
diff --git a/libs/permission/include/binder/IAppOpsCallback.h b/libs/permission/include/binder/IAppOpsCallback.h
deleted file mode 100644
index eb76f57..0000000
--- a/libs/permission/include/binder/IAppOpsCallback.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#ifndef __ANDROID_VNDK__
-
-#include <binder/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IAppOpsCallback : public IInterface
-{
-public:
-    DECLARE_META_INTERFACE(AppOpsCallback)
-
-    virtual void opChanged(int32_t op, const String16& packageName) = 0;
-
-    enum {
-        OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
-    };
-};
-
-// ----------------------------------------------------------------------
-
-class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
-{
-public:
-    // NOLINTNEXTLINE(google-default-arguments)
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-} // namespace android
-
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h
index 918fcdb..1468fd9 100644
--- a/libs/permission/include/binder/IAppOpsService.h
+++ b/libs/permission/include/binder/IAppOpsService.h
@@ -16,7 +16,8 @@
 
 #pragma once
 
-#include <binder/IAppOpsCallback.h>
+#include <com/android/internal/app/IAppOpsCallback.h>
+#include <com/android/internal/app/BnAppOpsCallback.h>
 #include <binder/IInterface.h>
 
 #include <optional>
@@ -27,6 +28,8 @@
 
 namespace android {
 
+using IAppOpsCallback = ::com::android::internal::app::IAppOpsCallback;
+
 // ----------------------------------------------------------------------
 
 class IAppOpsService : public IInterface
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
index 5c7f344..e96b17a 100644
--- a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -2,3 +2,4 @@
     class hal
     user graphics
     group automotive_evs
+    disabled
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c912937..8510698 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -7201,6 +7201,12 @@
     }
 }
 
+void InputDispatcher::setDisplayTopology(
+        const android::DisplayTopologyGraph& displayTopologyGraph) {
+    std::scoped_lock _l(mLock);
+    mWindowInfos.setDisplayTopology(displayTopologyGraph);
+}
+
 InputDispatcher::ConnectionManager::ConnectionManager(const sp<android::Looper>& looper)
       : mLooper(looper) {}
 
@@ -7336,6 +7342,11 @@
     mMaximumObscuringOpacityForTouch = opacity;
 }
 
+void InputDispatcher::DispatcherWindowInfo::setDisplayTopology(
+        const DisplayTopologyGraph& displayTopologyGraph) {
+    mTopology = displayTopologyGraph;
+}
+
 ftl::Flags<InputTarget::Flags> InputDispatcher::DispatcherTouchState::getTargetFlags(
         const sp<WindowInfoHandle>& targetWindow, vec2 targetPosition, bool isSplit,
         const DispatcherWindowInfo& windowInfos) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 52657d4..e76bd89 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -164,6 +164,8 @@
 
     void setInputMethodConnectionIsActive(bool isActive) override;
 
+    void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) override;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
@@ -291,6 +293,8 @@
 
         void setMaximumObscuringOpacityForTouch(float opacity);
 
+        void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph);
+
         // Get a reference to window handles by display, return an empty vector if not found.
         const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesForDisplay(
                 ui::LogicalDisplayId displayId) const;
@@ -341,6 +345,11 @@
         std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo>
                 mDisplayInfos;
         float mMaximumObscuringOpacityForTouch{1.0f};
+
+        // Topology is initialized with default-constructed value, which is an empty topology until
+        // we receive setDisplayTopology call. Meanwhile we will treat every display as an
+        // independent display.
+        DisplayTopologyGraph mTopology;
     };
 
     DispatcherWindowInfo mWindowInfos GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 463a952..ab039c3 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -24,6 +24,7 @@
 #include <android/os/InputEventInjectionSync.h>
 #include <gui/InputApplication.h>
 #include <gui/WindowInfo.h>
+#include <input/DisplayTopologyGraph.h>
 #include <input/InputDevice.h>
 #include <input/InputTransport.h>
 #include <unordered_map>
@@ -243,6 +244,11 @@
      * Notify the dispatcher that the state of the input method connection changed.
      */
     virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
+
+    /*
+     * Notify the dispatcher of the latest DisplayTopology.
+     */
+    virtual void setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) = 0;
 };
 
 } // namespace android
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 880df08..f38cf5a 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -22,6 +22,7 @@
         "android.hardware.sensors-V1-convert",
         "android.hardware.sensors-V3-ndk",
         "android.hardware.common-V2-ndk",
+        "framework-permission-aidl-cpp",
         "libsensor",
         "libfakeservicemanager",
         "libcutils",