Skip to main content

input_pipeline/
touch_binding.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::utils::{self, Position, Size};
7use crate::{Transport, metrics, mouse_binding};
8use anyhow::{Context, Error, format_err};
9use async_trait::async_trait;
10use fuchsia_inspect::ArrayProperty;
11use fuchsia_inspect::health::Reporter;
12use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
13use zx;
14
15use fidl_fuchsia_ui_input as fidl_ui_input;
16use fidl_next_fuchsia_ui_pointerinjector as pointerinjector;
17
18use metrics_registry::*;
19use sorted_vec_map::{SortedVecMap, SortedVecSet};
20
21/// A [`TouchScreenEvent`] represents a set of contacts and the phase those contacts are in.
22///
23/// For example, when a user touches a touch screen with two fingers, there will be two
24/// [`TouchContact`]s. When a user removes one finger, there will still be two contacts
25/// but one will be reported as removed.
26///
27/// The expected sequence for any given contact is:
28/// 1. [`fidl_fuchsia_ui_input::PointerEventPhase::Add`]
29/// 2. [`fidl_fuchsia_ui_input::PointerEventPhase::Down`]
30/// 3. 0 or more [`fidl_fuchsia_ui_input::PointerEventPhase::Move`]
31/// 4. [`fidl_fuchsia_ui_input::PointerEventPhase::Up`]
32/// 5. [`fidl_fuchsia_ui_input::PointerEventPhase::Remove`]
33///
34/// Additionally, a [`fidl_fuchsia_ui_input::PointerEventPhase::Cancel`] may be sent at any time
35/// signalling that the event is no longer directed towards the receiver.
36#[derive(Debug, PartialEq)]
37pub struct TouchScreenEvent {
38    /// Deprecated. To be removed with https://fxbug.dev/42155652.
39    /// The contacts associated with the touch event. For example, a two-finger touch would result
40    /// in one touch event with two [`TouchContact`]s.
41    ///
42    /// Contacts are grouped based on their current phase (e.g., down, move).
43    pub contacts: SortedVecMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
44
45    /// The contacts associated with the touch event. For example, a two-finger touch would result
46    /// in one touch event with two [`TouchContact`]s.
47    ///
48    /// Contacts are grouped based on their current phase (e.g., add, change).
49    pub injector_contacts: SortedVecMap<pointerinjector::EventPhase, Vec<TouchContact>>,
50
51    /// Indicates whether any touch buttons are pressed.
52    pub pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
53
54    /// The wake lease for this event.
55    pub wake_lease: Option<zx::EventPair>,
56}
57
58impl Clone for TouchScreenEvent {
59    fn clone(&self) -> Self {
60        log::debug!("TouchScreenEvent cloned without wake lease.");
61        Self {
62            contacts: self.contacts.clone(),
63            injector_contacts: self.injector_contacts.clone(),
64            pressed_buttons: self.pressed_buttons.clone(),
65            wake_lease: None,
66        }
67    }
68}
69
70impl Drop for TouchScreenEvent {
71    fn drop(&mut self) {
72        log::debug!("TouchScreenEvent dropped, had_wake_lease: {:?}", self.wake_lease);
73    }
74}
75
76impl TouchScreenEvent {
77    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
78        let contacts_clone = self.injector_contacts.clone();
79        node.record_child("injector_contacts", move |contacts_node| {
80            for (phase, contacts) in contacts_clone.iter() {
81                let phase_str = match pointerinjector::EventPhase::try_from(*phase) {
82                    Ok(pointerinjector::EventPhase::Add) => "add",
83                    Ok(pointerinjector::EventPhase::Change) => "change",
84                    Ok(pointerinjector::EventPhase::Remove) => "remove",
85                    Ok(pointerinjector::EventPhase::Cancel) => "cancel",
86                    Err(_) => unreachable!("invalid phase"),
87                };
88                contacts_node.record_child(phase_str, move |phase_node| {
89                    for contact in contacts.iter() {
90                        phase_node.record_child(contact.id.to_string(), move |contact_node| {
91                            if let Some(pressure) = contact.pressure {
92                                contact_node.record_int("pressure", pressure);
93                            }
94                            if let Some(contact_size) = contact.contact_size {
95                                contact_node.record_double(
96                                    "contact_width_mm",
97                                    f64::from(contact_size.width),
98                                );
99                                contact_node.record_double(
100                                    "contact_height_mm",
101                                    f64::from(contact_size.height),
102                                );
103                            }
104                        });
105                    }
106                });
107            }
108        });
109
110        let pressed_buttons_node =
111            node.create_string_array("pressed_buttons", self.pressed_buttons.len());
112        self.pressed_buttons.iter().enumerate().for_each(|(i, &ref button)| {
113            let button_name: String = match button {
114                fidl_next_fuchsia_input_report::TouchButton::Palm => "palm".into(),
115                unknown_value => {
116                    format!("unknown({:?})", unknown_value)
117                }
118            };
119            pressed_buttons_node.set(i, &button_name);
120        });
121        node.record(pressed_buttons_node);
122    }
123}
124
125/// A [`TouchpadEvent`] represents a set of contacts.
126///
127/// For example, when a user touches a touch screen with two fingers, there will be two
128/// [`TouchContact`]s in the vector.
129#[derive(Clone, Debug, PartialEq)]
130pub struct TouchpadEvent {
131    /// The contacts associated with the touch event. For example, a two-finger touch would result
132    /// in one touch event with two [`TouchContact`]s.
133    pub injector_contacts: Vec<TouchContact>,
134
135    /// The complete button state including this event.
136    pub pressed_buttons: SortedVecSet<mouse_binding::MouseButton>,
137}
138
139impl TouchpadEvent {
140    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
141        let pressed_buttons_node =
142            node.create_uint_array("pressed_buttons", self.pressed_buttons.len());
143        self.pressed_buttons.iter().enumerate().for_each(|(i, button)| {
144            pressed_buttons_node.set(i, *button);
145        });
146        node.record(pressed_buttons_node);
147
148        // Populate TouchpadEvent contact details.
149        let contacts_clone = self.injector_contacts.clone();
150        node.record_child("injector_contacts", move |contacts_node| {
151            for contact in contacts_clone.iter() {
152                contacts_node.record_child(contact.id.to_string(), move |contact_node| {
153                    if let Some(pressure) = contact.pressure {
154                        contact_node.record_int("pressure", pressure);
155                    }
156                    if let Some(contact_size) = contact.contact_size {
157                        contact_node
158                            .record_double("contact_width_mm", f64::from(contact_size.width));
159                        contact_node
160                            .record_double("contact_height_mm", f64::from(contact_size.height));
161                    }
162                })
163            }
164        });
165    }
166}
167
168/// [`TouchDeviceType`] indicates the type of touch device. Both Touch Screen and Windows Precision
169/// Touchpad send touch event from driver but need different process inside input pipeline.
170#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub enum TouchDeviceType {
172    TouchScreen,
173    WindowsPrecisionTouchpad,
174}
175
176/// A [`TouchContact`] represents a single contact (e.g., one touch of a multi-touch gesture) related
177/// to a touch event.
178#[derive(Clone, Copy, Debug, PartialEq)]
179pub struct TouchContact {
180    /// The identifier of the contact. Unique per touch device.
181    pub id: u32,
182
183    /// The position of the touch event, in the units of the associated
184    /// [`ContactDeviceDescriptor`]'s `range`.
185    pub position: Position,
186
187    /// The pressure associated with the contact, in the units of the associated
188    /// [`ContactDeviceDescriptor`]'s `pressure_range`.
189    pub pressure: Option<i64>,
190
191    /// The size of the touch event, in the units of the associated
192    /// [`ContactDeviceDescriptor`]'s `range`.
193    pub contact_size: Option<Size>,
194}
195
196impl Eq for TouchContact {}
197
198impl TryFrom<&fidl_next_fuchsia_input_report::ContactInputReport> for TouchContact {
199    type Error = anyhow::Error;
200
201    fn try_from(
202        fidl_contact: &fidl_next_fuchsia_input_report::ContactInputReport,
203    ) -> anyhow::Result<TouchContact> {
204        let contact_size =
205            if fidl_contact.contact_width.is_some() && fidl_contact.contact_height.is_some() {
206                Some(Size {
207                    width: fidl_contact.contact_width.unwrap() as f32,
208                    height: fidl_contact.contact_height.unwrap() as f32,
209                })
210            } else {
211                None
212            };
213
214        let id = fidl_contact.contact_id.context("contact_id is required")?;
215        let position_x = fidl_contact.position_x.context("position_x is required")?;
216        let position_y = fidl_contact.position_y.context("position_y is required")?;
217
218        Ok(TouchContact {
219            id,
220            position: Position { x: position_x as f32, y: position_y as f32 },
221            pressure: fidl_contact.pressure,
222            contact_size,
223        })
224    }
225}
226
227impl TryFrom<&fidl_next_fuchsia_input_report::wire::ContactInputReport<'_>> for TouchContact {
228    type Error = anyhow::Error;
229
230    fn try_from(
231        fidl_contact: &fidl_next_fuchsia_input_report::wire::ContactInputReport<'_>,
232    ) -> Result<Self, Self::Error> {
233        let contact_size =
234            if fidl_contact.contact_width().is_some() && fidl_contact.contact_height().is_some() {
235                Some(Size {
236                    width: fidl_contact.contact_width().map(|w| w.0).unwrap() as f32,
237                    height: fidl_contact.contact_height().map(|h| h.0).unwrap() as f32,
238                })
239            } else {
240                None
241            };
242
243        let id = fidl_contact.contact_id().map(|id| id.0).context("contact_id is required")?;
244        let position_x =
245            fidl_contact.position_x().map(|x| x.0).context("position_x is required")?;
246        let position_y =
247            fidl_contact.position_y().map(|y| y.0).context("position_y is required")?;
248
249        Ok(TouchContact {
250            id,
251            position: Position { x: position_x as f32, y: position_y as f32 },
252            pressure: fidl_contact.pressure().map(|p| p.0),
253            contact_size,
254        })
255    }
256}
257
258#[derive(Clone, Debug, Eq, PartialEq)]
259pub struct TouchScreenDeviceDescriptor {
260    /// The id of the connected touch screen input device.
261    pub device_id: u32,
262
263    /// The descriptors for the possible contacts associated with the device.
264    pub contacts: Vec<ContactDeviceDescriptor>,
265}
266
267#[derive(Clone, Debug, Eq, PartialEq)]
268pub struct TouchpadDeviceDescriptor {
269    /// The id of the connected touchpad input device.
270    pub device_id: u32,
271
272    /// The descriptors for the possible contacts associated with the device.
273    pub contacts: Vec<ContactDeviceDescriptor>,
274}
275
276#[derive(Clone, Debug, Eq, PartialEq)]
277enum TouchDeviceDescriptor {
278    TouchScreen(TouchScreenDeviceDescriptor),
279    Touchpad(TouchpadDeviceDescriptor),
280}
281
282/// A [`ContactDeviceDescriptor`] describes the possible values touch contact properties can take on.
283///
284/// This descriptor can be used, for example, to determine where on a screen a touch made contact.
285///
286/// # Example
287///
288/// ```
289/// // Determine the scaling factor between the display and the touch device's x range.
290/// let scaling_factor =
291///     display_width / (contact_descriptor._x_range.end - contact_descriptor._x_range.start);
292/// // Use the scaling factor to scale the contact report's x position.
293/// let hit_location =
294///     scaling_factor * (contact_report.position_x - contact_descriptor._x_range.start);
295#[derive(Clone, Debug, Eq, PartialEq)]
296pub struct ContactDeviceDescriptor {
297    /// The range of possible x values for this touch contact.
298    pub x_range: fidl_fuchsia_input::Range,
299
300    /// The range of possible y values for this touch contact.
301    pub y_range: fidl_fuchsia_input::Range,
302
303    /// The unit of measure for `x_range`.
304    pub x_unit: fidl_fuchsia_input::Unit,
305
306    /// The unit of measure for `y_range`.
307    pub y_unit: fidl_fuchsia_input::Unit,
308
309    /// The range of possible pressure values for this touch contact.
310    pub pressure_range: Option<fidl_fuchsia_input::Range>,
311
312    /// The range of possible widths for this touch contact.
313    pub width_range: Option<fidl_fuchsia_input::Range>,
314
315    /// The range of possible heights for this touch contact.
316    pub height_range: Option<fidl_fuchsia_input::Range>,
317}
318
319/// A [`TouchBinding`] represents a connection to a touch input device.
320///
321/// The [`TouchBinding`] parses and exposes touch descriptor properties (e.g., the range of
322/// possible x values for touch contacts) for the device it is associated with.
323/// It also parses [`InputReport`]s from the device, and sends them to the device binding owner over
324/// `event_sender`.
325pub struct TouchBinding {
326    /// The channel to stream InputEvents to.
327    event_sender: UnboundedSender<Vec<InputEvent>>,
328
329    /// Holds information about this device.
330    device_descriptor: TouchDeviceDescriptor,
331
332    /// Touch device type of the touch device.
333    touch_device_type: TouchDeviceType,
334
335    /// Proxy to the device.
336    device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
337}
338
339#[async_trait]
340impl input_device::InputDeviceBinding for TouchBinding {
341    fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>> {
342        self.event_sender.clone()
343    }
344
345    fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
346        match self.device_descriptor.clone() {
347            TouchDeviceDescriptor::TouchScreen(desc) => {
348                input_device::InputDeviceDescriptor::TouchScreen(desc)
349            }
350            TouchDeviceDescriptor::Touchpad(desc) => {
351                input_device::InputDeviceDescriptor::Touchpad(desc)
352            }
353        }
354    }
355}
356
357impl TouchBinding {
358    /// Creates a new [`InputDeviceBinding`] from the `device_proxy`.
359    ///
360    /// The binding will start listening for input reports immediately and send new InputEvents
361    /// to the device binding owner over `input_event_sender`.
362    ///
363    /// # Parameters
364    /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to.
365    /// - `device_id`: The id of the connected touch device.
366    /// - `input_event_sender`: The channel to send new InputEvents to.
367    /// - `device_node`: The inspect node for this device binding
368    /// - `metrics_logger`: The metrics logger.
369    ///
370    /// # Errors
371    /// If there was an error binding to the proxy.
372    pub async fn new(
373        device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
374        device_id: u32,
375        input_event_sender: UnboundedSender<Vec<InputEvent>>,
376        device_node: fuchsia_inspect::Node,
377        feature_flags: input_device::InputPipelineFeatureFlags,
378        metrics_logger: metrics::MetricsLogger,
379    ) -> Result<Self, Error> {
380        let (device_binding, mut inspect_status) =
381            Self::bind_device(device_proxy.clone(), device_id, input_event_sender, device_node)
382                .await?;
383        device_binding
384            .set_touchpad_mode(true)
385            .await
386            .with_context(|| format!("enabling touchpad mode for device {}", device_id))?;
387        inspect_status.health_node.set_ok();
388        input_device::initialize_report_stream(
389            device_proxy,
390            device_binding.get_device_descriptor(),
391            device_binding.input_event_sender(),
392            inspect_status,
393            metrics_logger,
394            feature_flags,
395            Self::process_reports,
396        );
397
398        Ok(device_binding)
399    }
400
401    /// Binds the provided input device to a new instance of `Self`.
402    ///
403    /// # Parameters
404    /// - `device`: The device to use to initialize the binding.
405    /// - `device_id`: The id of the connected touch device.
406    /// - `input_event_sender`: The channel to send new InputEvents to.
407    /// - `device_node`: The inspect node for this device binding
408    ///
409    /// # Errors
410    /// If the device descriptor could not be retrieved, or the descriptor could not be parsed
411    /// correctly.
412    async fn bind_device(
413        device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
414        device_id: u32,
415        input_event_sender: UnboundedSender<Vec<InputEvent>>,
416        device_node: fuchsia_inspect::Node,
417    ) -> Result<(Self, InputDeviceStatus), Error> {
418        let mut input_device_status = InputDeviceStatus::new(device_node);
419        let device_descriptor: fidl_next_fuchsia_input_report::DeviceDescriptor = match device_proxy
420            .get_descriptor()
421            .await
422        {
423            Ok(res) => res.descriptor,
424            Err(_) => {
425                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
426                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
427            }
428        };
429
430        let touch_device_type = get_device_type(&device_proxy).await;
431
432        match device_descriptor.touch {
433            Some(fidl_next_fuchsia_input_report::TouchDescriptor {
434                input:
435                    Some(fidl_next_fuchsia_input_report::TouchInputDescriptor {
436                        contacts: Some(contact_descriptors),
437                        max_contacts: _,
438                        touch_type: _,
439                        buttons: _,
440                        ..
441                    }),
442                ..
443            }) => Ok((
444                TouchBinding {
445                    event_sender: input_event_sender,
446                    device_descriptor: match touch_device_type {
447                        TouchDeviceType::TouchScreen => {
448                            TouchDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
449                                device_id,
450                                contacts: contact_descriptors
451                                    .iter()
452                                    .map(TouchBinding::parse_contact_descriptor)
453                                    .filter_map(Result::ok)
454                                    .collect(),
455                            })
456                        }
457                        TouchDeviceType::WindowsPrecisionTouchpad => {
458                            TouchDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
459                                device_id,
460                                contacts: contact_descriptors
461                                    .iter()
462                                    .map(TouchBinding::parse_contact_descriptor)
463                                    .filter_map(Result::ok)
464                                    .collect(),
465                            })
466                        }
467                    },
468                    touch_device_type,
469                    device_proxy,
470                },
471                input_device_status,
472            )),
473            descriptor => {
474                input_device_status
475                    .health_node
476                    .set_unhealthy("Touch Device Descriptor failed to parse.");
477                Err(format_err!("Touch Descriptor failed to parse: \n {:?}", descriptor))
478            }
479        }
480    }
481
482    async fn set_touchpad_mode(&self, enable: bool) -> Result<(), Error> {
483        match self.touch_device_type {
484            TouchDeviceType::TouchScreen => Ok(()),
485            TouchDeviceType::WindowsPrecisionTouchpad => {
486                // `get_feature_report` to only modify the input_mode and
487                // keep other feature as is.
488                let mut report = match self.device_proxy.get_feature_report().await? {
489                    Ok(res) => res.report,
490                    Err(e) => return Err(format_err!("get_feature_report failed: {}", e)),
491                };
492                let mut touch = report
493                    .touch
494                    .unwrap_or_else(fidl_next_fuchsia_input_report::TouchFeatureReport::default);
495                touch.input_mode = match enable {
496                            true => Some(fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection),
497                            false => Some(fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection),
498                        };
499                report.touch = Some(touch);
500                match self.device_proxy.set_feature_report(&report).await? {
501                    Ok(_) => {
502                        // TODO(https://fxbug.dev/42056283): Remove log message.
503                        log::info!("touchpad: set touchpad_enabled to {}", enable);
504                        Ok(())
505                    }
506                    Err(e) => Err(format_err!("set_feature_report failed: {}", e)),
507                }
508            }
509        }
510    }
511
512    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
513    ///
514    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
515    ///
516    /// # Parameters
517    /// - `reports`: The incoming [`InputReport`].
518    /// - `previous_report`: The previous [`InputReport`] seen for the same device. This can be
519    ///                    used to determine, for example, which keys are no longer present in
520    ///                    a keyboard report to generate key released events. If `None`, no
521    ///                    previous report was found.
522    /// - `device_descriptor`: The descriptor for the input device generating the input reports.
523    /// - `input_event_sender`: The sender for the device binding's input event stream.
524    ///
525    /// # Returns
526    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
527    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
528    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
529    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
530    /// binding does not generate InputEvents asynchronously, this will be `None`.
531    ///
532    /// The returned [`InputReport`] is guaranteed to have no `wake_lease`.
533    fn process_reports(
534        reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
535        previous_state: Option<input_device::PreviousDeviceState>,
536        device_descriptor: &input_device::InputDeviceDescriptor,
537        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
538        inspect_status: &InputDeviceStatus,
539        metrics_logger: &metrics::MetricsLogger,
540        feature_flags: &input_device::InputPipelineFeatureFlags,
541    ) -> (Option<input_device::PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>) {
542        fuchsia_trace::duration!(
543            "input",
544            "touch-binding-process-report",
545            "num_reports" => reports.len(),
546        );
547        match device_descriptor {
548            input_device::InputDeviceDescriptor::TouchScreen(_) => process_touch_screen_reports(
549                reports,
550                previous_state,
551                device_descriptor,
552                input_event_sender,
553                inspect_status,
554                metrics_logger,
555                feature_flags.enable_merge_touch_events,
556            ),
557            input_device::InputDeviceDescriptor::Touchpad(_) => {
558                // TODO(b/512925135): support touchpad in starnix
559                (previous_state, None)
560            }
561            _ => (previous_state, None),
562        }
563    }
564
565    /// Parses a fidl_fuchsia_input_report contact descriptor into a [`ContactDeviceDescriptor`]
566    ///
567    /// # Parameters
568    /// - `contact_device_descriptor`: The contact descriptor to parse.
569    ///
570    /// # Errors
571    /// If the contact description fails to parse because required fields aren't present.
572    fn parse_contact_descriptor(
573        contact_device_descriptor: &fidl_next_fuchsia_input_report::ContactInputDescriptor,
574    ) -> Result<ContactDeviceDescriptor, Error> {
575        match contact_device_descriptor {
576            fidl_next_fuchsia_input_report::ContactInputDescriptor {
577                position_x: Some(x_axis),
578                position_y: Some(y_axis),
579                pressure: pressure_axis,
580                contact_width: width_axis,
581                contact_height: height_axis,
582                ..
583            } => Ok(ContactDeviceDescriptor {
584                x_range: utils::range_to_old(&x_axis.range),
585                y_range: utils::range_to_old(&y_axis.range),
586                x_unit: utils::unit_to_old(&x_axis.unit),
587                y_unit: utils::unit_to_old(&y_axis.unit),
588                pressure_range: pressure_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
589                width_range: width_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
590                height_range: height_axis.as_ref().map(|axis| utils::range_to_old(&axis.range)),
591            }),
592            descriptor => {
593                Err(format_err!("Touch Contact Descriptor failed to parse: \n {:?}", descriptor))
594            }
595        }
596    }
597}
598
599fn is_move_only(event: &InputEvent) -> bool {
600    matches!(
601        &event.device_event,
602        input_device::InputDeviceEvent::TouchScreen(event)
603            if event
604                .injector_contacts
605                .get(&pointerinjector::EventPhase::Add)
606                .map_or(true, |c| c.is_empty())
607                && event
608                    .injector_contacts
609                    .get(&pointerinjector::EventPhase::Remove)
610                    .map_or(true, |c| c.is_empty())
611                && event
612                    .injector_contacts
613                    .get(&pointerinjector::EventPhase::Cancel)
614                    .map_or(true, |c| c.is_empty())
615    )
616}
617
618fn has_pressed_buttons(event: &InputEvent) -> bool {
619    match &event.device_event {
620        input_device::InputDeviceEvent::TouchScreen(event) => !event.pressed_buttons.is_empty(),
621        _ => false,
622    }
623}
624
625fn process_touch_screen_reports(
626    reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
627    mut previous_state: Option<input_device::PreviousDeviceState>,
628    device_descriptor: &input_device::InputDeviceDescriptor,
629    input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
630    inspect_status: &InputDeviceStatus,
631    metrics_logger: &metrics::MetricsLogger,
632    enable_merge_touch_events: bool,
633) -> (Option<input_device::PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>) {
634    let num_reports = reports.len();
635    let mut batch: Vec<InputEvent> = Vec::with_capacity(num_reports);
636    for report in reports {
637        inspect_status.count_received_report_wire(report);
638        let (prev_state, event) = process_single_touch_screen_report(
639            report,
640            previous_state,
641            device_descriptor,
642            inspect_status,
643            metrics_logger,
644        );
645        previous_state = prev_state;
646        if let Some(event) = event {
647            batch.push(event);
648        }
649    }
650
651    if !batch.is_empty() {
652        if enable_merge_touch_events {
653            // Pre-calculate move-only status for all events
654            let mut is_event_move_only: Vec<bool> = Vec::with_capacity(batch.len());
655            let mut pressed_buttons: Vec<bool> = Vec::with_capacity(batch.len());
656            for event in &batch {
657                is_event_move_only.push(is_move_only(event));
658                pressed_buttons.push(has_pressed_buttons(event));
659            }
660            let size_of_batch = batch.len();
661
662            // Merge consecutive move-only events into a single event.
663            let mut merged_batch = Vec::with_capacity(size_of_batch);
664
665            // Use into_iter().enumerate() to move elements without cloning
666            for (i, current_event) in batch.into_iter().enumerate() {
667                let current_is_move = is_event_move_only[i];
668                let current_pressed_buttons = pressed_buttons[i];
669                let is_last_event = i == size_of_batch - 1;
670
671                // Check if the NEXT event is also move-only
672                let next_is_move =
673                    if i + 1 < size_of_batch { is_event_move_only[i + 1] } else { false };
674
675                let next_pressed_buttons = if i + 1 < size_of_batch {
676                    pressed_buttons[i + 1]
677                } else {
678                    current_pressed_buttons
679                };
680
681                // If both are move-only, skip the current one (it's redundant).
682                // always keep the last event
683                if !is_last_event
684                    // both are move-only
685                    && (current_is_move && next_is_move)
686                    // same pressed buttons
687                    && (current_pressed_buttons == next_pressed_buttons)
688                {
689                    continue;
690                }
691
692                merged_batch.push(current_event);
693            }
694
695            batch = merged_batch;
696        }
697
698        let events_to_send: Vec<InputEvent> = {
699            fuchsia_trace::duration!("input", "prepare_events_to_send");
700            batch
701                .into_iter()
702                .map(|event| {
703                    // Unwrap is safe because trace_id is set when the event is created.
704                    // This unwrap will not move the trace_id out of the event because trace_id has
705                    // Copy trait.
706                    let trace_id: fuchsia_trace::Id = event.trace_id.unwrap();
707                    fuchsia_trace::flow_begin!("input", "event_in_input_pipeline", trace_id);
708                    event
709                })
710                .collect()
711        };
712        fuchsia_trace::instant!(
713            "input",
714            "events_to_input_handlers",
715            fuchsia_trace::Scope::Thread,
716            "num_reports" => num_reports,
717            "num_events_generated" => events_to_send.len()
718        );
719
720        // Record inspect data before sending, as unbounded_send consumes the vector.
721        inspect_status.count_generated_events(&events_to_send);
722
723        if let Err(e) = input_event_sender.unbounded_send(events_to_send) {
724            metrics_logger.log_error(
725                InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
726                std::format!("Failed to send TouchScreenEvent with error: {:?}", e),
727            );
728        }
729    }
730    (previous_state, None)
731}
732
733fn process_single_touch_screen_report(
734    report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
735    previous_state: Option<input_device::PreviousDeviceState>,
736    device_descriptor: &input_device::InputDeviceDescriptor,
737    inspect_status: &InputDeviceStatus,
738    metrics_logger: &metrics::MetricsLogger,
739) -> (Option<input_device::PreviousDeviceState>, Option<InputEvent>) {
740    fuchsia_trace::flow_end!(
741        "input",
742        "input_report",
743        report.trace_id().map(|x| x.0).unwrap_or(0).into()
744    );
745
746    // Extract the wake_lease early to prevent it from leaking. If this is moved
747    // below an early return, the lease could accidentally be stored inside
748    // `previous_report`, which would prevent the system from suspending.
749    let wake_lease = utils::duplicate_wake_lease(report.wake_lease());
750
751    // Input devices can have multiple types so ensure `report` is a TouchInputReport.
752    let touch_report = match report.touch() {
753        Some(touch) => touch,
754        None => {
755            inspect_status.count_filtered_report();
756            return (previous_state, None);
757        }
758    };
759
760    let (previous_contacts, previous_buttons): (
761        SortedVecMap<u32, TouchContact>,
762        Vec<fidl_next_fuchsia_input_report::TouchButton>,
763    ) = match &previous_state {
764        Some(input_device::PreviousDeviceState::TouchScreen {
765            active_contacts,
766            pressed_buttons,
767        }) => {
768            let contacts =
769                SortedVecMap::from_iter(active_contacts.iter().map(|c| (c.id, c.clone())));
770            (contacts, pressed_buttons.clone())
771        }
772        _ => (SortedVecMap::new(), vec![]),
773    };
774    let (current_contacts, current_buttons): (
775        SortedVecMap<u32, TouchContact>,
776        Vec<fidl_next_fuchsia_input_report::TouchButton>,
777    ) = touch_contacts_and_buttons_from_touch_report_wire(touch_report, metrics_logger);
778
779    if previous_contacts.is_empty()
780        && current_contacts.is_empty()
781        && previous_buttons.is_empty()
782        && current_buttons.is_empty()
783    {
784        inspect_status.count_filtered_report();
785        return (previous_state, None);
786    }
787
788    // Contacts which exist only in current.
789    let added_contacts: Vec<TouchContact> = Vec::from_iter(
790        current_contacts
791            .iter()
792            .map(|(_, v)| v.clone())
793            .filter(|contact| !previous_contacts.contains_key(&contact.id)),
794    );
795    // Contacts which exist in both previous and current.
796    let moved_contacts: Vec<TouchContact> = Vec::from_iter(
797        current_contacts
798            .iter()
799            .map(|(_, v)| v.clone())
800            .filter(|contact| previous_contacts.contains_key(&contact.id)),
801    );
802    // Contacts which exist only in previous.
803    let removed_contacts: Vec<TouchContact> =
804        Vec::from_iter(previous_contacts.iter().map(|(_, v)| v.clone()).filter(|contact| {
805            current_buttons.is_empty()
806                && previous_buttons.is_empty()
807                && !current_contacts.contains_key(&contact.id)
808        }));
809
810    let active_contacts: Vec<TouchContact> = if current_contacts.is_empty()
811        && !previous_contacts.is_empty()
812        && (!current_buttons.is_empty() || !previous_buttons.is_empty())
813    {
814        previous_contacts.values().cloned().collect()
815    } else {
816        added_contacts.iter().chain(moved_contacts.iter()).cloned().collect()
817    };
818
819    let trace_id = fuchsia_trace::Id::new();
820    let event = create_touch_screen_event(
821        SortedVecMap::from_iter(vec![
822            (fidl_ui_input::PointerEventPhase::Add, added_contacts.clone()),
823            (fidl_ui_input::PointerEventPhase::Down, added_contacts.clone()),
824            (fidl_ui_input::PointerEventPhase::Move, moved_contacts.clone()),
825            (fidl_ui_input::PointerEventPhase::Up, removed_contacts.clone()),
826            (fidl_ui_input::PointerEventPhase::Remove, removed_contacts.clone()),
827        ]),
828        SortedVecMap::from_iter(vec![
829            (pointerinjector::EventPhase::Add, added_contacts),
830            (pointerinjector::EventPhase::Change, moved_contacts),
831            (pointerinjector::EventPhase::Remove, removed_contacts),
832        ]),
833        current_buttons.clone(),
834        device_descriptor,
835        trace_id,
836        wake_lease,
837    );
838
839    let next_previous_state = input_device::PreviousDeviceState::TouchScreen {
840        active_contacts,
841        pressed_buttons: current_buttons,
842    };
843
844    (Some(next_previous_state), Some(event))
845}
846
847fn touch_contacts_and_buttons_from_touch_report_wire(
848    touch_report: &fidl_next_fuchsia_input_report::wire::TouchInputReport<'_>,
849    metrics_logger: &metrics::MetricsLogger,
850) -> (SortedVecMap<u32, TouchContact>, Vec<fidl_next_fuchsia_input_report::TouchButton>) {
851    let mut contacts = Vec::new();
852    if let Some(unwrapped_contacts) = touch_report.contacts() {
853        for contact in unwrapped_contacts.iter() {
854            match TouchContact::try_from(contact) {
855                Ok(c) => contacts.push(c),
856                Err(e) => {
857                    metrics_logger.log_warn(
858                        InputPipelineErrorMetricDimensionEvent::TouchReportContactMissingField,
859                        std::format!("failed to convert touch contact: {:?}", e),
860                    );
861                }
862            }
863        }
864    } else {
865        metrics_logger.log_warn(
866            InputPipelineErrorMetricDimensionEvent::TouchReportMissingContact,
867            "contacts missing in touch input report",
868        );
869    }
870
871    let pressed_buttons = touch_report
872        .pressed_buttons()
873        .map(|buttons| buttons.iter().map(|&b| fidl_next::FromWire::from_wire(b)).collect())
874        .unwrap_or_default();
875
876    (
877        SortedVecMap::from_iter(contacts.into_iter().map(|contact| (contact.id, contact))),
878        pressed_buttons,
879    )
880}
881
882/// Create a TouchScreenEvent.
883///
884/// # Parameters
885/// - `contacts`: The contact points relevant to the new TouchScreenEvent.
886/// - `injector_contacts`: The contact points relevant to the new TouchScreenEvent, used to send
887///                        pointer events into Scenic.
888/// - `device_descriptor`: The descriptor for the input device generating the input reports.
889/// - `trace_id`: The trace id to distinguish the event.
890/// - `wake_lease`: The wake lease to send with the event.
891fn create_touch_screen_event(
892    contacts: SortedVecMap<fidl_ui_input::PointerEventPhase, Vec<TouchContact>>,
893    injector_contacts: SortedVecMap<pointerinjector::EventPhase, Vec<TouchContact>>,
894    pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
895    device_descriptor: &input_device::InputDeviceDescriptor,
896    trace_id: fuchsia_trace::Id,
897    wake_lease: Option<zx::EventPair>,
898) -> InputEvent {
899    input_device::InputEvent {
900        device_event: input_device::InputDeviceEvent::TouchScreen(TouchScreenEvent {
901            contacts,
902            injector_contacts,
903            pressed_buttons,
904            wake_lease,
905        }),
906        device_descriptor: device_descriptor.clone(),
907        event_time: zx::MonotonicInstant::get(),
908        handled: Handled::No,
909        trace_id: Some(trace_id),
910    }
911}
912
913/// [`get_device_type`] check if the touch device is a touchscreen or Windows Precision Touchpad.
914///
915/// Windows Precision Touchpad reports `MouseCollection` or `WindowsPrecisionTouchpadCollection`
916/// in `TouchFeatureReport`. Fallback all error responses on `get_feature_report` to TouchScreen
917/// because some touch screen does not report this method.
918async fn get_device_type(
919    input_device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
920) -> TouchDeviceType {
921    match input_device.get_feature_report().await {
922        Ok(Ok(fidl_next_fuchsia_input_report::InputDeviceGetFeatureReportResponse {
923            report: fidl_next_fuchsia_input_report::FeatureReport {
924                touch:
925                    Some(fidl_next_fuchsia_input_report::TouchFeatureReport {
926                        input_mode:
927                            Some(
928                                fidl_next_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection
929                                | fidl_next_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection,
930                            ),
931                        ..
932                    }),
933                ..
934            }
935        })) => TouchDeviceType::WindowsPrecisionTouchpad,
936        _ => TouchDeviceType::TouchScreen,
937    }
938}
939
940#[cfg(test)]
941mod tests {
942    use super::*;
943    use crate::testing_utilities::{
944        self, create_touch_contact, create_touch_input_report, create_touch_screen_event,
945        create_touch_screen_event_with_buttons, spawn_input_stream_handler,
946    };
947    use crate::utils::Position;
948    use assert_matches::assert_matches;
949    use diagnostics_assertions::AnyProperty;
950    use fuchsia_async as fasync;
951    use futures::StreamExt;
952    use pretty_assertions::assert_eq;
953    use test_case::test_case;
954
955    #[fasync::run_singlethreaded(test)]
956    async fn process_empty_reports() {
957        let report_time = zx::MonotonicInstant::get().into_nanos();
958        let report =
959            create_touch_input_report(vec![], /* pressed_buttons= */ None, report_time);
960
961        let descriptor =
962            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
963                device_id: 1,
964                contacts: vec![],
965            });
966        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
967
968        let inspector = fuchsia_inspect::Inspector::default();
969        let test_node = inspector.root().create_child("TestDevice_Touch");
970        let mut inspect_status = InputDeviceStatus::new(test_node);
971        inspect_status.health_node.set_ok();
972
973        let previous_state = input_device::PreviousDeviceState::TouchScreen {
974            active_contacts: vec![],
975            pressed_buttons: vec![],
976        };
977
978        let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
979        let (returned_state, _) = TouchBinding::process_reports(
980            &reports_wire,
981            Some(previous_state),
982            &descriptor,
983            &mut event_sender,
984            &inspect_status,
985            &metrics::MetricsLogger::default(),
986            &input_device::InputPipelineFeatureFlags::default(),
987        );
988        assert!(returned_state.is_some());
989        assert_eq!(
990            returned_state.unwrap(),
991            input_device::PreviousDeviceState::TouchScreen {
992                active_contacts: vec![],
993                pressed_buttons: vec![]
994            }
995        );
996
997        // Assert there are no pending events on the receiver.
998        let event = event_receiver.try_next();
999        assert!(event.is_err());
1000
1001        diagnostics_assertions::assert_data_tree!(inspector, root: {
1002            "TestDevice_Touch": contains {
1003                reports_received_count: 1u64,
1004                reports_filtered_count: 1u64,
1005                events_generated: 0u64,
1006                last_received_timestamp_ns: report_time as u64,
1007                last_generated_timestamp_ns: 0u64,
1008                "fuchsia.inspect.Health": {
1009                    status: "OK",
1010                    // Timestamp value is unpredictable and not relevant in this context,
1011                    // so we only assert that the property is present.
1012                    start_timestamp_nanos: AnyProperty
1013                },
1014            }
1015        });
1016    }
1017
1018    // Tests that a input report with a new contact generates an event with an add and a down.
1019    #[fasync::run_singlethreaded(test)]
1020    async fn add_and_down() {
1021        const TOUCH_ID: u32 = 2;
1022
1023        let descriptor =
1024            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1025                device_id: 1,
1026                contacts: vec![],
1027            });
1028        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1029
1030        let contact = fidl_fuchsia_input_report::ContactInputReport {
1031            contact_id: Some(TOUCH_ID),
1032            position_x: Some(0),
1033            position_y: Some(0),
1034            pressure: None,
1035            contact_width: None,
1036            contact_height: None,
1037            ..Default::default()
1038        };
1039        let reports = vec![create_touch_input_report(
1040            vec![contact],
1041            /* pressed_buttons= */ None,
1042            event_time_i64,
1043        )];
1044
1045        let expected_events = vec![create_touch_screen_event(
1046            SortedVecMap::from_iter(vec![
1047                (
1048                    fidl_ui_input::PointerEventPhase::Add,
1049                    vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1050                ),
1051                (
1052                    fidl_ui_input::PointerEventPhase::Down,
1053                    vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1054                ),
1055            ]),
1056            event_time_u64,
1057            &descriptor,
1058        )];
1059
1060        assert_input_report_sequence_generates_events!(
1061            input_reports: reports,
1062            expected_events: expected_events,
1063            device_descriptor: descriptor,
1064            device_type: TouchBinding,
1065        );
1066    }
1067
1068    // Tests that up and remove events are sent when a touch is released.
1069    #[fasync::run_singlethreaded(test)]
1070    async fn up_and_remove() {
1071        const TOUCH_ID: u32 = 2;
1072
1073        let descriptor =
1074            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1075                device_id: 1,
1076                contacts: vec![],
1077            });
1078        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1079
1080        let contact = fidl_fuchsia_input_report::ContactInputReport {
1081            contact_id: Some(TOUCH_ID),
1082            position_x: Some(0),
1083            position_y: Some(0),
1084            pressure: None,
1085            contact_width: None,
1086            contact_height: None,
1087            ..Default::default()
1088        };
1089        let reports = vec![
1090            create_touch_input_report(
1091                vec![contact],
1092                /* pressed_buttons= */ None,
1093                event_time_i64,
1094            ),
1095            create_touch_input_report(vec![], /* pressed_buttons= */ None, event_time_i64),
1096        ];
1097
1098        let expected_events = vec![
1099            create_touch_screen_event(
1100                SortedVecMap::from_iter(vec![
1101                    (
1102                        fidl_ui_input::PointerEventPhase::Add,
1103                        vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1104                    ),
1105                    (
1106                        fidl_ui_input::PointerEventPhase::Down,
1107                        vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1108                    ),
1109                ]),
1110                event_time_u64,
1111                &descriptor,
1112            ),
1113            create_touch_screen_event(
1114                SortedVecMap::from_iter(vec![
1115                    (
1116                        fidl_ui_input::PointerEventPhase::Up,
1117                        vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1118                    ),
1119                    (
1120                        fidl_ui_input::PointerEventPhase::Remove,
1121                        vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1122                    ),
1123                ]),
1124                event_time_u64,
1125                &descriptor,
1126            ),
1127        ];
1128
1129        assert_input_report_sequence_generates_events!(
1130            input_reports: reports,
1131            expected_events: expected_events,
1132            device_descriptor: descriptor,
1133            device_type: TouchBinding,
1134        );
1135    }
1136
1137    // Tests that a move generates the correct event.
1138    #[fasync::run_singlethreaded(test)]
1139    async fn add_down_move() {
1140        const TOUCH_ID: u32 = 2;
1141        let first = Position { x: 10.0, y: 30.0 };
1142        let second = Position { x: first.x * 2.0, y: first.y * 2.0 };
1143
1144        let descriptor =
1145            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1146                device_id: 1,
1147                contacts: vec![],
1148            });
1149        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1150
1151        let first_contact = fidl_fuchsia_input_report::ContactInputReport {
1152            contact_id: Some(TOUCH_ID),
1153            position_x: Some(first.x as i64),
1154            position_y: Some(first.y as i64),
1155            pressure: None,
1156            contact_width: None,
1157            contact_height: None,
1158            ..Default::default()
1159        };
1160        let second_contact = fidl_fuchsia_input_report::ContactInputReport {
1161            contact_id: Some(TOUCH_ID),
1162            position_x: Some(first.x as i64 * 2),
1163            position_y: Some(first.y as i64 * 2),
1164            pressure: None,
1165            contact_width: None,
1166            contact_height: None,
1167            ..Default::default()
1168        };
1169
1170        let reports = vec![
1171            create_touch_input_report(
1172                vec![first_contact],
1173                /* pressed_buttons= */ None,
1174                event_time_i64,
1175            ),
1176            create_touch_input_report(
1177                vec![second_contact],
1178                /* pressed_buttons= */ None,
1179                event_time_i64,
1180            ),
1181        ];
1182
1183        let expected_events = vec![
1184            create_touch_screen_event(
1185                SortedVecMap::from_iter(vec![
1186                    (
1187                        fidl_ui_input::PointerEventPhase::Add,
1188                        vec![create_touch_contact(TOUCH_ID, first)],
1189                    ),
1190                    (
1191                        fidl_ui_input::PointerEventPhase::Down,
1192                        vec![create_touch_contact(TOUCH_ID, first)],
1193                    ),
1194                ]),
1195                event_time_u64,
1196                &descriptor,
1197            ),
1198            create_touch_screen_event(
1199                SortedVecMap::from_iter(vec![(
1200                    fidl_ui_input::PointerEventPhase::Move,
1201                    vec![create_touch_contact(TOUCH_ID, second)],
1202                )]),
1203                event_time_u64,
1204                &descriptor,
1205            ),
1206        ];
1207
1208        assert_input_report_sequence_generates_events!(
1209            input_reports: reports,
1210            expected_events: expected_events,
1211            device_descriptor: descriptor,
1212            device_type: TouchBinding,
1213        );
1214    }
1215
1216    #[fasync::run_singlethreaded(test)]
1217    async fn sent_event_has_trace_id() {
1218        let report_time = zx::MonotonicInstant::get().into_nanos();
1219        let contact = fidl_fuchsia_input_report::ContactInputReport {
1220            contact_id: Some(222),
1221            position_x: Some(333),
1222            position_y: Some(444),
1223            ..Default::default()
1224        };
1225        let report =
1226            create_touch_input_report(vec![contact], /* pressed_buttons= */ None, report_time);
1227
1228        let descriptor =
1229            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1230                device_id: 1,
1231                contacts: vec![],
1232            });
1233        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1234
1235        let inspector = fuchsia_inspect::Inspector::default();
1236        let test_node = inspector.root().create_child("TestDevice_Touch");
1237        let mut inspect_status = InputDeviceStatus::new(test_node);
1238        inspect_status.health_node.set_ok();
1239
1240        let previous_state = input_device::PreviousDeviceState::TouchScreen {
1241            active_contacts: vec![],
1242            pressed_buttons: vec![],
1243        };
1244
1245        let reports_wire = crate::testing_utilities::reports_to_wire(vec![report]);
1246        let _ = TouchBinding::process_reports(
1247            &reports_wire,
1248            Some(previous_state),
1249            &descriptor,
1250            &mut event_sender,
1251            &inspect_status,
1252            &metrics::MetricsLogger::default(),
1253            &input_device::InputPipelineFeatureFlags::default(),
1254        );
1255        assert_matches!(event_receiver.try_next(), Ok(Some(events)) if events.len() == 1 && events[0].trace_id.is_some());
1256    }
1257
1258    #[fuchsia::test(allow_stalls = false)]
1259    async fn enables_touchpad_mode_automatically() {
1260        let (set_feature_report_sender, set_feature_report_receiver) =
1261            futures::channel::mpsc::unbounded();
1262        let (input_device_proxy, _task) = spawn_input_stream_handler(move |input_device_request| {
1263            let set_feature_report_sender = set_feature_report_sender.clone();
1264            async move {
1265                match input_device_request {
1266                    fidl_fuchsia_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1267                        let _ = responder.send(&get_touchpad_device_descriptor(
1268                            true, /* has_mouse_descriptor */
1269                        ));
1270                    }
1271                    fidl_fuchsia_input_report::InputDeviceRequest::GetFeatureReport {
1272                        responder,
1273                    } => {
1274                        let _ = responder.send(Ok(&fidl_fuchsia_input_report::FeatureReport {
1275                            touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1276                                input_mode: Some(
1277                                    fidl_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection,
1278                                ),
1279                                ..Default::default()
1280                            }),
1281                            ..Default::default()
1282                        }));
1283                    }
1284                    fidl_fuchsia_input_report::InputDeviceRequest::SetFeatureReport {
1285                        responder,
1286                        report,
1287                    } => {
1288                        match set_feature_report_sender.unbounded_send(report) {
1289                            Ok(_) => {
1290                                let _ = responder.send(Ok(()));
1291                            }
1292                            Err(e) => {
1293                                panic!("try_send set_feature_report_request failed: {}", e);
1294                            }
1295                        };
1296                    }
1297                    fidl_fuchsia_input_report::InputDeviceRequest::GetInputReportsReader {
1298                        ..
1299                    } => {
1300                        // Do not panic as `initialize_report_stream()` will call this protocol.
1301                    }
1302                    r => panic!("unsupported request {:?}", r),
1303                }
1304            }
1305        });
1306
1307        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1308
1309        // Create a test inspect node as required by TouchBinding::new()
1310        let inspector = fuchsia_inspect::Inspector::default();
1311        let test_node = inspector.root().create_child("test_node");
1312
1313        // Create a `TouchBinding` to exercise its call to `SetFeatureReport`. But drop
1314        // the binding immediately, so that `set_feature_report_receiver.collect()`
1315        // does not hang.
1316        TouchBinding::new(
1317            input_device_proxy,
1318            0,
1319            device_event_sender,
1320            test_node,
1321            input_device::InputPipelineFeatureFlags::default(),
1322            metrics::MetricsLogger::default(),
1323        )
1324        .await
1325        .unwrap();
1326        assert_matches!(
1327            set_feature_report_receiver.collect::<Vec<_>>().await.as_slice(),
1328            [fidl_fuchsia_input_report::FeatureReport {
1329                touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1330                    input_mode: Some(
1331                        fidl_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection
1332                    ),
1333                    ..
1334                }),
1335                ..
1336            }]
1337        );
1338    }
1339
1340    #[test_case(true, None, TouchDeviceType::TouchScreen; "touch screen")]
1341    #[test_case(false, None, TouchDeviceType::TouchScreen; "no mouse descriptor, no touch_input_mode")]
1342    #[test_case(true, Some(fidl_fuchsia_input_report::TouchConfigurationInputMode::MouseCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in mouse mode")]
1343    #[test_case(true, Some(fidl_fuchsia_input_report::TouchConfigurationInputMode::WindowsPrecisionTouchpadCollection), TouchDeviceType::WindowsPrecisionTouchpad; "touchpad in touchpad mode")]
1344    #[fuchsia::test(allow_stalls = false)]
1345    async fn identifies_correct_touch_device_type(
1346        has_mouse_descriptor: bool,
1347        touch_input_mode: Option<fidl_fuchsia_input_report::TouchConfigurationInputMode>,
1348        expect_touch_device_type: TouchDeviceType,
1349    ) {
1350        let (input_device_proxy, _task) =
1351            spawn_input_stream_handler(move |input_device_request| async move {
1352                match input_device_request {
1353                    fidl_fuchsia_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1354                        let _ =
1355                            responder.send(&get_touchpad_device_descriptor(has_mouse_descriptor));
1356                    }
1357                    fidl_fuchsia_input_report::InputDeviceRequest::GetFeatureReport {
1358                        responder,
1359                    } => {
1360                        let _ = responder.send(Ok(&fidl_fuchsia_input_report::FeatureReport {
1361                            touch: Some(fidl_fuchsia_input_report::TouchFeatureReport {
1362                                input_mode: touch_input_mode,
1363                                ..Default::default()
1364                            }),
1365                            ..Default::default()
1366                        }));
1367                    }
1368                    fidl_fuchsia_input_report::InputDeviceRequest::SetFeatureReport {
1369                        responder,
1370                        ..
1371                    } => {
1372                        let _ = responder.send(Ok(()));
1373                    }
1374                    r => panic!("unsupported request {:?}", r),
1375                }
1376            });
1377
1378        let (device_event_sender, _) = futures::channel::mpsc::unbounded();
1379
1380        // Create a test inspect node as required by TouchBinding::new()
1381        let inspector = fuchsia_inspect::Inspector::default();
1382        let test_node = inspector.root().create_child("test_node");
1383
1384        let binding = TouchBinding::new(
1385            input_device_proxy,
1386            0,
1387            device_event_sender,
1388            test_node,
1389            input_device::InputPipelineFeatureFlags::default(),
1390            metrics::MetricsLogger::default(),
1391        )
1392        .await
1393        .unwrap();
1394        pretty_assertions::assert_eq!(binding.touch_device_type, expect_touch_device_type);
1395    }
1396
1397    /// Returns an |fidl_fuchsia_input_report::DeviceDescriptor| for
1398    /// touchpad related tests.
1399    fn get_touchpad_device_descriptor(
1400        has_mouse_descriptor: bool,
1401    ) -> fidl_fuchsia_input_report::DeviceDescriptor {
1402        fidl_fuchsia_input_report::DeviceDescriptor {
1403            mouse: match has_mouse_descriptor {
1404                true => Some(fidl_fuchsia_input_report::MouseDescriptor::default()),
1405                false => None,
1406            },
1407            touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
1408                input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
1409                    contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
1410                        position_x: Some(fidl_fuchsia_input::Axis {
1411                            range: fidl_fuchsia_input::Range { min: 1, max: 2 },
1412                            unit: fidl_fuchsia_input::Unit {
1413                                type_: fidl_fuchsia_input::UnitType::None,
1414                                exponent: 0,
1415                            },
1416                        }),
1417                        position_y: Some(fidl_fuchsia_input::Axis {
1418                            range: fidl_fuchsia_input::Range { min: 2, max: 3 },
1419                            unit: fidl_fuchsia_input::Unit {
1420                                type_: fidl_fuchsia_input::UnitType::Other,
1421                                exponent: 100000,
1422                            },
1423                        }),
1424                        pressure: Some(fidl_fuchsia_input::Axis {
1425                            range: fidl_fuchsia_input::Range { min: 3, max: 4 },
1426                            unit: fidl_fuchsia_input::Unit {
1427                                type_: fidl_fuchsia_input::UnitType::Grams,
1428                                exponent: -991,
1429                            },
1430                        }),
1431                        contact_width: Some(fidl_fuchsia_input::Axis {
1432                            range: fidl_fuchsia_input::Range { min: 5, max: 6 },
1433                            unit: fidl_fuchsia_input::Unit {
1434                                type_: fidl_fuchsia_input::UnitType::EnglishAngularVelocity,
1435                                exponent: 123,
1436                            },
1437                        }),
1438                        contact_height: Some(fidl_fuchsia_input::Axis {
1439                            range: fidl_fuchsia_input::Range { min: 7, max: 8 },
1440                            unit: fidl_fuchsia_input::Unit {
1441                                type_: fidl_fuchsia_input::UnitType::Pascals,
1442                                exponent: 100,
1443                            },
1444                        }),
1445                        ..Default::default()
1446                    }]),
1447                    ..Default::default()
1448                }),
1449                ..Default::default()
1450            }),
1451            ..Default::default()
1452        }
1453    }
1454
1455    // Tests that a pressed button with no contacts generates an event with the
1456    // button.
1457    #[test_case(true; "merge touch events enabled")]
1458    #[test_case(false; "merge touch events disabled")]
1459    #[fasync::run_singlethreaded(test)]
1460    async fn send_pressed_button_no_contact(enable_merge_touch_events: bool) {
1461        let descriptor =
1462            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1463                device_id: 1,
1464                contacts: vec![],
1465            });
1466        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1467
1468        let reports = vec![create_touch_input_report(
1469            vec![],
1470            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1471            event_time_i64,
1472        )];
1473
1474        let expected_events = vec![create_touch_screen_event_with_buttons(
1475            SortedVecMap::new(),
1476            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1477            event_time_u64,
1478            &descriptor,
1479        )];
1480
1481        assert_input_report_sequence_generates_events_with_feature_flags!(
1482            input_reports: reports,
1483            expected_events: expected_events,
1484            device_descriptor: descriptor,
1485            device_type: TouchBinding,
1486            feature_flags: input_device::InputPipelineFeatureFlags {
1487                enable_merge_touch_events,
1488                ..Default::default()
1489            },
1490        );
1491    }
1492
1493    // Tests that a pressed button with a contact generates an event with
1494    // contact and button.
1495    #[test_case(true; "merge touch events enabled")]
1496    #[test_case(false; "merge touch events disabled")]
1497    #[fasync::run_singlethreaded(test)]
1498    async fn send_pressed_button_with_contact(enable_merge_touch_events: bool) {
1499        const TOUCH_ID: u32 = 2;
1500
1501        let descriptor =
1502            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1503                device_id: 1,
1504                contacts: vec![],
1505            });
1506        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1507
1508        let contact = fidl_fuchsia_input_report::ContactInputReport {
1509            contact_id: Some(TOUCH_ID),
1510            position_x: Some(0),
1511            position_y: Some(0),
1512            pressure: None,
1513            contact_width: None,
1514            contact_height: None,
1515            ..Default::default()
1516        };
1517        let reports = vec![create_touch_input_report(
1518            vec![contact],
1519            Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1520            event_time_i64,
1521        )];
1522
1523        let expected_events = vec![create_touch_screen_event_with_buttons(
1524            SortedVecMap::from_iter(vec![
1525                (
1526                    fidl_ui_input::PointerEventPhase::Add,
1527                    vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1528                ),
1529                (
1530                    fidl_ui_input::PointerEventPhase::Down,
1531                    vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1532                ),
1533            ]),
1534            vec![fidl_fuchsia_input_report::TouchButton::Palm],
1535            event_time_u64,
1536            &descriptor,
1537        )];
1538
1539        assert_input_report_sequence_generates_events_with_feature_flags!(
1540            input_reports: reports,
1541            expected_events: expected_events,
1542            device_descriptor: descriptor,
1543            device_type: TouchBinding,
1544            feature_flags: input_device::InputPipelineFeatureFlags {
1545                enable_merge_touch_events,
1546                ..Default::default()
1547            },
1548        );
1549    }
1550
1551    // Tests that multiple pressed buttons with contacts generates an event
1552    // with contact and buttons.
1553    #[test_case(true; "merge touch events enabled")]
1554    #[test_case(false; "merge touch events disabled")]
1555    #[fasync::run_singlethreaded(test)]
1556    async fn send_multiple_pressed_buttons_with_contact(enable_merge_touch_events: bool) {
1557        const TOUCH_ID: u32 = 2;
1558
1559        let descriptor =
1560            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1561                device_id: 1,
1562                contacts: vec![],
1563            });
1564        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1565
1566        let contact = fidl_fuchsia_input_report::ContactInputReport {
1567            contact_id: Some(TOUCH_ID),
1568            position_x: Some(0),
1569            position_y: Some(0),
1570            pressure: None,
1571            contact_width: None,
1572            contact_height: None,
1573            ..Default::default()
1574        };
1575        let reports = vec![create_touch_input_report(
1576            vec![contact],
1577            Some(vec![
1578                fidl_fuchsia_input_report::TouchButton::Palm,
1579                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1580            ]),
1581            event_time_i64,
1582        )];
1583
1584        let expected_events = vec![create_touch_screen_event_with_buttons(
1585            SortedVecMap::from_iter(vec![
1586                (
1587                    fidl_ui_input::PointerEventPhase::Add,
1588                    vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1589                ),
1590                (
1591                    fidl_ui_input::PointerEventPhase::Down,
1592                    vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1593                ),
1594            ]),
1595            vec![
1596                fidl_fuchsia_input_report::TouchButton::Palm,
1597                fidl_fuchsia_input_report::TouchButton::__SourceBreaking { unknown_ordinal: 2 },
1598            ],
1599            event_time_u64,
1600            &descriptor,
1601        )];
1602
1603        assert_input_report_sequence_generates_events_with_feature_flags!(
1604            input_reports: reports,
1605            expected_events: expected_events,
1606            device_descriptor: descriptor,
1607            device_type: TouchBinding,
1608            feature_flags: input_device::InputPipelineFeatureFlags {
1609                enable_merge_touch_events,
1610                ..Default::default()
1611            },
1612        );
1613    }
1614
1615    // Tests that no buttons and no contacts generates no events.
1616    #[test_case(true; "merge touch events enabled")]
1617    #[test_case(false; "merge touch events disabled")]
1618    #[fasync::run_singlethreaded(test)]
1619    async fn send_no_buttons_no_contacts(enable_merge_touch_events: bool) {
1620        let descriptor =
1621            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1622                device_id: 1,
1623                contacts: vec![],
1624            });
1625        let (event_time_i64, _) = testing_utilities::event_times();
1626
1627        let reports = vec![create_touch_input_report(vec![], Some(vec![]), event_time_i64)];
1628
1629        let expected_events: Vec<input_device::InputEvent> = vec![];
1630
1631        assert_input_report_sequence_generates_events_with_feature_flags!(
1632            input_reports: reports,
1633            expected_events: expected_events,
1634            device_descriptor: descriptor,
1635            device_type: TouchBinding,
1636            feature_flags: input_device::InputPipelineFeatureFlags {
1637                enable_merge_touch_events,
1638                ..Default::default()
1639            },
1640        );
1641    }
1642
1643    // Tests a buttons event after a contact event does not remove contacts.
1644    #[test_case(true; "merge touch events enabled")]
1645    #[test_case(false; "merge touch events disabled")]
1646    #[fasync::run_singlethreaded(test)]
1647    async fn send_button_does_not_remove_contacts(enable_merge_touch_events: bool) {
1648        const TOUCH_ID: u32 = 2;
1649
1650        let descriptor =
1651            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1652                device_id: 1,
1653                contacts: vec![],
1654            });
1655        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
1656
1657        let contact = fidl_fuchsia_input_report::ContactInputReport {
1658            contact_id: Some(TOUCH_ID),
1659            position_x: Some(0),
1660            position_y: Some(0),
1661            pressure: None,
1662            contact_width: None,
1663            contact_height: None,
1664            ..Default::default()
1665        };
1666        let reports = vec![
1667            create_touch_input_report(vec![contact], None, event_time_i64),
1668            create_touch_input_report(
1669                vec![],
1670                Some(vec![fidl_fuchsia_input_report::TouchButton::Palm]),
1671                event_time_i64,
1672            ),
1673            create_touch_input_report(vec![], Some(vec![]), event_time_i64),
1674        ];
1675
1676        let expected_events = vec![
1677            create_touch_screen_event_with_buttons(
1678                SortedVecMap::from_iter(vec![
1679                    (
1680                        fidl_ui_input::PointerEventPhase::Add,
1681                        vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1682                    ),
1683                    (
1684                        fidl_ui_input::PointerEventPhase::Down,
1685                        vec![create_touch_contact(TOUCH_ID, Position { x: 0.0, y: 0.0 })],
1686                    ),
1687                ]),
1688                vec![],
1689                event_time_u64,
1690                &descriptor,
1691            ),
1692            create_touch_screen_event_with_buttons(
1693                SortedVecMap::new(),
1694                vec![fidl_fuchsia_input_report::TouchButton::Palm],
1695                event_time_u64,
1696                &descriptor,
1697            ),
1698            create_touch_screen_event_with_buttons(
1699                SortedVecMap::new(),
1700                vec![],
1701                event_time_u64,
1702                &descriptor,
1703            ),
1704        ];
1705
1706        assert_input_report_sequence_generates_events_with_feature_flags!(
1707            input_reports: reports,
1708            expected_events: expected_events,
1709            device_descriptor: descriptor,
1710            device_type: TouchBinding,
1711            feature_flags: input_device::InputPipelineFeatureFlags {
1712                enable_merge_touch_events,
1713                ..Default::default()
1714            },
1715        );
1716    }
1717
1718    #[fasync::run_singlethreaded(test)]
1719    async fn process_reports_batches_events() {
1720        const TOUCH_ID: u32 = 2;
1721
1722        let descriptor =
1723            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1724                device_id: 1,
1725                contacts: vec![],
1726            });
1727        let (event_time_i64, _) = testing_utilities::event_times();
1728
1729        let contact1 = fidl_fuchsia_input_report::ContactInputReport {
1730            contact_id: Some(TOUCH_ID),
1731            position_x: Some(0),
1732            position_y: Some(0),
1733            ..Default::default()
1734        };
1735        let contact2 = fidl_fuchsia_input_report::ContactInputReport {
1736            contact_id: Some(TOUCH_ID),
1737            position_x: Some(10),
1738            position_y: Some(10),
1739            ..Default::default()
1740        };
1741        let reports = vec![
1742            create_touch_input_report(vec![contact1], None, event_time_i64),
1743            create_touch_input_report(vec![contact2], None, event_time_i64),
1744        ];
1745
1746        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1747
1748        let inspector = fuchsia_inspect::Inspector::default();
1749        let test_node = inspector.root().create_child("TestDevice_Touch");
1750        let mut inspect_status = InputDeviceStatus::new(test_node);
1751        inspect_status.health_node.set_ok();
1752
1753        let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1754        let _ = TouchBinding::process_reports(
1755            &reports_wire,
1756            None,
1757            &descriptor,
1758            &mut event_sender,
1759            &inspect_status,
1760            &metrics::MetricsLogger::default(),
1761            &input_device::InputPipelineFeatureFlags::default(),
1762        );
1763
1764        // Expect EXACTLY one batch containing two events.
1765        let batch = event_receiver.try_next().expect("Expected a batch of events");
1766        let events = batch.expect("Expected events in the batch");
1767        assert_eq!(events.len(), 2);
1768
1769        // Verify no more batches.
1770        assert!(event_receiver.try_next().is_err());
1771    }
1772
1773    #[fasync::run_singlethreaded(test)]
1774    async fn process_reports_merges_touch_events_when_enabled() {
1775        const TOUCH_ID: u32 = 2;
1776        let descriptor =
1777            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1778                device_id: 1,
1779                contacts: vec![],
1780            });
1781        let (event_time_i64, _) = testing_utilities::event_times();
1782
1783        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1784            contact_id: Some(TOUCH_ID),
1785            position_x: Some(0),
1786            position_y: Some(0),
1787            ..Default::default()
1788        };
1789        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1790            contact_id: Some(TOUCH_ID),
1791            position_x: Some(10),
1792            position_y: Some(10),
1793            ..Default::default()
1794        };
1795        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1796            contact_id: Some(TOUCH_ID),
1797            position_x: Some(20),
1798            position_y: Some(20),
1799            ..Default::default()
1800        };
1801        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1802            contact_id: Some(TOUCH_ID),
1803            position_x: Some(30),
1804            position_y: Some(30),
1805            ..Default::default()
1806        };
1807        let reports = vec![
1808            create_touch_input_report(vec![contact_add], None, event_time_i64),
1809            create_touch_input_report(vec![contact_move1], None, event_time_i64),
1810            create_touch_input_report(vec![contact_move2], None, event_time_i64),
1811            create_touch_input_report(vec![contact_move3], None, event_time_i64),
1812            create_touch_input_report(vec![], None, event_time_i64),
1813        ];
1814
1815        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1816        let inspector = fuchsia_inspect::Inspector::default();
1817        let mut inspect_status =
1818            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1819        inspect_status.health_node.set_ok();
1820
1821        let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1822        let _ = TouchBinding::process_reports(
1823            &reports_wire,
1824            None,
1825            &descriptor,
1826            &mut event_sender,
1827            &inspect_status,
1828            &metrics::MetricsLogger::default(),
1829            &input_device::InputPipelineFeatureFlags {
1830                enable_merge_touch_events: true,
1831                ..Default::default()
1832            },
1833        );
1834
1835        let batch = event_receiver.try_next().unwrap().unwrap();
1836
1837        // Expected events: Add, Move(30), Remove.
1838        assert_eq!(batch.len(), 3);
1839
1840        // Verify Add event
1841        assert_matches!(
1842            &batch[0].device_event,
1843            input_device::InputDeviceEvent::TouchScreen(event)
1844                if event.injector_contacts.get(&pointerinjector::EventPhase::Add).is_some()
1845        );
1846        // Verify Move event (merged to the last one)
1847        assert_matches!(
1848            &batch[1].device_event,
1849            input_device::InputDeviceEvent::TouchScreen(event)
1850                if event.injector_contacts.get(&pointerinjector::EventPhase::Change).map(|c| c[0].position.x) == Some(30.0)
1851        );
1852        // Verify Remove event
1853        assert_matches!(
1854            &batch[2].device_event,
1855            input_device::InputDeviceEvent::TouchScreen(event)
1856                if event.injector_contacts.get(&pointerinjector::EventPhase::Remove).is_some()
1857        );
1858    }
1859
1860    #[fasync::run_singlethreaded(test)]
1861    async fn process_reports_does_not_merge_touch_events_when_disabled() {
1862        const TOUCH_ID: u32 = 2;
1863        let descriptor =
1864            input_device::InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
1865                device_id: 1,
1866                contacts: vec![],
1867            });
1868        let (event_time_i64, _) = testing_utilities::event_times();
1869
1870        let contact_add = fidl_fuchsia_input_report::ContactInputReport {
1871            contact_id: Some(TOUCH_ID),
1872            position_x: Some(0),
1873            position_y: Some(0),
1874            ..Default::default()
1875        };
1876        let contact_move1 = fidl_fuchsia_input_report::ContactInputReport {
1877            contact_id: Some(TOUCH_ID),
1878            position_x: Some(10),
1879            position_y: Some(10),
1880            ..Default::default()
1881        };
1882        let contact_move2 = fidl_fuchsia_input_report::ContactInputReport {
1883            contact_id: Some(TOUCH_ID),
1884            position_x: Some(20),
1885            position_y: Some(20),
1886            ..Default::default()
1887        };
1888        let contact_move3 = fidl_fuchsia_input_report::ContactInputReport {
1889            contact_id: Some(TOUCH_ID),
1890            position_x: Some(30),
1891            position_y: Some(30),
1892            ..Default::default()
1893        };
1894        let reports = vec![
1895            create_touch_input_report(vec![contact_add], None, event_time_i64),
1896            create_touch_input_report(vec![contact_move1], None, event_time_i64),
1897            create_touch_input_report(vec![contact_move2], None, event_time_i64),
1898            create_touch_input_report(vec![contact_move3], None, event_time_i64),
1899            create_touch_input_report(vec![], None, event_time_i64),
1900        ];
1901
1902        let (mut event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1903        let inspector = fuchsia_inspect::Inspector::default();
1904        let mut inspect_status =
1905            InputDeviceStatus::new(inspector.root().create_child("TestDevice_Touch"));
1906        inspect_status.health_node.set_ok();
1907
1908        let reports_wire = crate::testing_utilities::reports_to_wire(reports);
1909        let _ = TouchBinding::process_reports(
1910            &reports_wire,
1911            None,
1912            &descriptor,
1913            &mut event_sender,
1914            &inspect_status,
1915            &metrics::MetricsLogger::default(),
1916            &input_device::InputPipelineFeatureFlags {
1917                enable_merge_touch_events: false,
1918                ..Default::default()
1919            },
1920        );
1921
1922        let batch = event_receiver.try_next().unwrap().unwrap();
1923
1924        // Expected events: Add, Move(10), Move(20), Move(30), Remove.
1925        assert_eq!(batch.len(), 5);
1926    }
1927}