input_pipeline/
touch_injector_handler.rs

1// Copyright 2021 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
5#![warn(clippy::await_holding_refcell_ref)]
6use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
7use crate::utils::{Position, Size};
8use crate::{input_device, metrics, touch_binding};
9use anyhow::{Context, Error, Result};
10use async_trait::async_trait;
11use async_utils::hanging_get::client::HangingGetStream;
12use fidl::endpoints::{Proxy, create_proxy};
13use fidl::{AsHandleRef, HandleBased};
14use fuchsia_component::client::connect_to_protocol;
15use fuchsia_inspect::health::Reporter;
16use futures::channel::mpsc;
17use futures::stream::StreamExt;
18use metrics_registry::*;
19use std::cell::RefCell;
20use std::collections::HashMap;
21use std::rc::Rc;
22use {
23    fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
24    fidl_fuchsia_ui_pointerinjector as pointerinjector,
25    fidl_fuchsia_ui_pointerinjector_configuration as pointerinjector_config,
26    fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
27};
28
29/// An input handler that parses touch events and forwards them to Scenic through the
30/// fidl_fuchsia_pointerinjector protocols.
31pub struct TouchInjectorHandler {
32    /// The mutable fields of this handler.
33    mutable_state: RefCell<MutableState>,
34
35    /// The scope and coordinate system of injection.
36    /// See fidl_fuchsia_pointerinjector::Context for more details.
37    context_view_ref: fidl_fuchsia_ui_views::ViewRef,
38
39    /// The region where dispatch is attempted for injected events.
40    /// See fidl_fuchsia_pointerinjector::Target for more details.
41    target_view_ref: fidl_fuchsia_ui_views::ViewRef,
42
43    /// The size of the display associated with the touch device, used to convert
44    /// coordinates from the touch input report to device coordinates (which is what
45    /// Scenic expects).
46    display_size: Size,
47
48    /// The FIDL proxy to register new injectors.
49    injector_registry_proxy: pointerinjector::RegistryProxy,
50
51    /// The FIDL proxy used to get configuration details for pointer injection.
52    configuration_proxy: pointerinjector_config::SetupProxy,
53
54    /// The inventory of this handler's Inspect status.
55    pub inspect_status: InputHandlerStatus,
56
57    /// The metrics logger.
58    metrics_logger: metrics::MetricsLogger,
59}
60
61#[derive(Debug)]
62struct MutableState {
63    /// A rectangular region that directs injected events into a target.
64    /// See fidl_fuchsia_pointerinjector::Viewport for more details.
65    viewport: Option<pointerinjector::Viewport>,
66
67    /// The injectors registered with Scenic, indexed by their device ids.
68    injectors: HashMap<u32, pointerinjector::DeviceProxy>,
69
70    /// The touch button listeners, key referenced by proxy channel's raw handle.
71    pub listeners: HashMap<u32, fidl_ui_policy::TouchButtonsListenerProxy>,
72
73    /// The last TouchButtonsEvent sent to all listeners.
74    /// This is used to send new listeners the state of the touchscreen buttons.
75    pub last_button_event: Option<fidl_ui_input::TouchButtonsEvent>,
76
77    pub send_event_task_tracker: LocalTaskTracker,
78}
79
80#[async_trait(?Send)]
81impl UnhandledInputHandler for TouchInjectorHandler {
82    async fn handle_unhandled_input_event(
83        self: Rc<Self>,
84        mut unhandled_input_event: input_device::UnhandledInputEvent,
85    ) -> Vec<input_device::InputEvent> {
86        fuchsia_trace::duration!(c"input", c"touch_injector_handler");
87        match unhandled_input_event {
88            input_device::UnhandledInputEvent {
89                device_event: input_device::InputDeviceEvent::TouchScreen(ref mut touch_event),
90                device_descriptor:
91                    input_device::InputDeviceDescriptor::TouchScreen(ref touch_device_descriptor),
92                event_time,
93                trace_id,
94            } => {
95                self.inspect_status.count_received_event(&event_time);
96                fuchsia_trace::duration!(c"input", c"touch_injector_handler[processing]");
97                if let Some(trace_id) = trace_id {
98                    fuchsia_trace::flow_end!(c"input", c"event_in_input_pipeline", trace_id.into());
99                }
100                if touch_event.injector_contacts.values().all(|vec| vec.is_empty()) {
101                    let mut touch_buttons_event = Self::create_touch_buttons_event(
102                        touch_event,
103                        event_time,
104                        &touch_device_descriptor,
105                    );
106
107                    // Send the event if the touch buttons are supported.
108                    self.send_event_to_listeners(&touch_buttons_event).await;
109
110                    // Store the sent event without any wake leases.
111                    std::mem::drop(touch_buttons_event.wake_lease.take());
112                    self.mutable_state.borrow_mut().last_button_event = Some(touch_buttons_event);
113                } else if touch_event.pressed_buttons.is_empty() {
114                    // Create a new injector if this is the first time seeing device_id.
115                    if let Err(e) = self.ensure_injector_registered(&touch_device_descriptor).await
116                    {
117                        self.metrics_logger.log_error(
118                        InputPipelineErrorMetricDimensionEvent::TouchInjectorEnsureInjectorRegisteredFailed,
119                        std::format!("ensure_injector_registered failed: {}", e));
120                    }
121
122                    // Handle the event.
123                    if let Err(e) = self
124                        .send_event_to_scenic(touch_event, &touch_device_descriptor, event_time)
125                        .await
126                    {
127                        self.metrics_logger.log_error(
128                        InputPipelineErrorMetricDimensionEvent::TouchInjectorSendEventToScenicFailed,
129                        std::format!("send_event_to_scenic failed: {}", e));
130                    }
131                }
132
133                // Consume the input event.
134                self.inspect_status.count_handled_event();
135                vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
136            }
137            _ => vec![input_device::InputEvent::from(unhandled_input_event)],
138        }
139    }
140
141    fn set_handler_healthy(self: std::rc::Rc<Self>) {
142        self.inspect_status.health_node.borrow_mut().set_ok();
143    }
144
145    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
146        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
147    }
148}
149
150impl TouchInjectorHandler {
151    /// Creates a new touch handler that holds touch pointer injectors.
152    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
153    /// Example:
154    /// let handler = TouchInjectorHandler::new(display_size).await?;
155    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
156    ///
157    /// # Parameters
158    /// - `display_size`: The size of the associated touch display.
159    ///
160    /// # Errors
161    /// If unable to connect to pointerinjector protocols.
162    pub async fn new(
163        display_size: Size,
164        input_handlers_node: &fuchsia_inspect::Node,
165        metrics_logger: metrics::MetricsLogger,
166    ) -> Result<Rc<Self>, Error> {
167        let configuration_proxy = connect_to_protocol::<pointerinjector_config::SetupMarker>()?;
168        let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
169
170        Self::new_handler(
171            configuration_proxy,
172            injector_registry_proxy,
173            display_size,
174            input_handlers_node,
175            metrics_logger,
176        )
177        .await
178    }
179
180    /// Creates a new touch handler that holds touch pointer injectors.
181    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
182    /// Example:
183    /// let handler = TouchInjectorHandler::new_with_config_proxy(config_proxy, display_size).await?;
184    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
185    ///
186    /// # Parameters
187    /// - `configuration_proxy`: A proxy used to get configuration details for pointer
188    ///    injection.
189    /// - `display_size`: The size of the associated touch display.
190    ///
191    /// # Errors
192    /// If unable to get injection view refs from `configuration_proxy`.
193    /// If unable to connect to pointerinjector Registry protocol.
194    pub async fn new_with_config_proxy(
195        configuration_proxy: pointerinjector_config::SetupProxy,
196        display_size: Size,
197        input_handlers_node: &fuchsia_inspect::Node,
198        metrics_logger: metrics::MetricsLogger,
199    ) -> Result<Rc<Self>, Error> {
200        let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
201        Self::new_handler(
202            configuration_proxy,
203            injector_registry_proxy,
204            display_size,
205            input_handlers_node,
206            metrics_logger,
207        )
208        .await
209    }
210
211    /// Creates a new touch handler that holds touch pointer injectors.
212    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
213    /// Example:
214    /// let handler = TouchInjectorHandler::new_handler(None, None, display_size).await?;
215    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
216    ///
217    /// # Parameters
218    /// - `configuration_proxy`: A proxy used to get configuration details for pointer
219    ///    injection.
220    /// - `injector_registry_proxy`: A proxy used to register new pointer injectors.  If
221    ///    none is provided, connect to protocol routed to this component.
222    /// - `display_size`: The size of the associated touch display.
223    ///
224    /// # Errors
225    /// If unable to get injection view refs from `configuration_proxy`.
226    async fn new_handler(
227        configuration_proxy: pointerinjector_config::SetupProxy,
228        injector_registry_proxy: pointerinjector::RegistryProxy,
229        display_size: Size,
230        input_handlers_node: &fuchsia_inspect::Node,
231        metrics_logger: metrics::MetricsLogger,
232    ) -> Result<Rc<Self>, Error> {
233        // Get the context and target views to inject into.
234        let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
235
236        let inspect_status = InputHandlerStatus::new(
237            input_handlers_node,
238            "touch_injector_handler",
239            /* generates_events */ false,
240        );
241        let handler = Rc::new(Self {
242            mutable_state: RefCell::new(MutableState {
243                viewport: None,
244                injectors: HashMap::new(),
245                listeners: HashMap::new(),
246                last_button_event: None,
247                send_event_task_tracker: LocalTaskTracker::new(),
248            }),
249            context_view_ref,
250            target_view_ref,
251            display_size,
252            injector_registry_proxy,
253            configuration_proxy,
254            inspect_status,
255            metrics_logger,
256        });
257
258        Ok(handler)
259    }
260
261    fn clone_event(event: &fidl_ui_input::TouchButtonsEvent) -> fidl_ui_input::TouchButtonsEvent {
262        fidl_ui_input::TouchButtonsEvent {
263            event_time: event.event_time,
264            device_info: event.device_info.clone(),
265            pressed_buttons: event.pressed_buttons.clone(),
266            wake_lease: event.wake_lease.as_ref().map(|lease| {
267                fidl::EventPair::from_handle(
268                    lease
269                        .as_handle_ref()
270                        .duplicate(zx::Rights::SAME_RIGHTS)
271                        .expect("failed to duplicate event pair")
272                        .into_handle(),
273                )
274            }),
275            ..Default::default()
276        }
277    }
278
279    /// Adds a new pointer injector and tracks it in `self.injectors` if one doesn't exist at
280    /// `touch_descriptor.device_id`.
281    ///
282    /// # Parameters
283    /// - `touch_descriptor`: The descriptor of the new touch device.
284    async fn ensure_injector_registered(
285        self: &Rc<Self>,
286        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
287    ) -> Result<(), anyhow::Error> {
288        if self.mutable_state.borrow().injectors.contains_key(&touch_descriptor.device_id) {
289            return Ok(());
290        }
291
292        // Create a new injector.
293        let (device_proxy, device_server) = create_proxy::<pointerinjector::DeviceMarker>();
294        let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
295            .context("Failed to duplicate context view ref.")?;
296        let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
297            .context("Failed to duplicate target view ref.")?;
298        let viewport = self.mutable_state.borrow().viewport.clone();
299        if viewport.is_none() {
300            // An injector without a viewport is not valid. The event will be dropped
301            // since the handler will not have a registered injector to inject into.
302            return Err(anyhow::format_err!(
303                "Received a touch event without a viewport to inject into."
304            ));
305        }
306        let config = pointerinjector::Config {
307            device_id: Some(touch_descriptor.device_id),
308            device_type: Some(pointerinjector::DeviceType::Touch),
309            context: Some(pointerinjector::Context::View(context)),
310            target: Some(pointerinjector::Target::View(target)),
311            viewport,
312            dispatch_policy: Some(pointerinjector::DispatchPolicy::TopHitAndAncestorsInTarget),
313            scroll_v_range: None,
314            scroll_h_range: None,
315            buttons: None,
316            ..Default::default()
317        };
318
319        // Keep track of the injector.
320        self.mutable_state.borrow_mut().injectors.insert(touch_descriptor.device_id, device_proxy);
321
322        // Register the new injector.
323        self.injector_registry_proxy
324            .register(config, device_server)
325            .await
326            .context("Failed to register injector.")?;
327        log::info!("Registered injector with device id {:?}", touch_descriptor.device_id);
328
329        Ok(())
330    }
331
332    /// Sends the given event to Scenic.
333    ///
334    /// # Parameters
335    /// - `touch_event`: The touch event to send to Scenic.
336    /// - `touch_descriptor`: The descriptor for the device that sent the touch event.
337    /// - `event_time`: The time when the event was first recorded.
338    async fn send_event_to_scenic(
339        &self,
340        touch_event: &mut touch_binding::TouchScreenEvent,
341        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
342        event_time: zx::MonotonicInstant,
343    ) -> Result<(), anyhow::Error> {
344        // The order in which events are sent to clients.
345        let ordered_phases = vec![
346            pointerinjector::EventPhase::Add,
347            pointerinjector::EventPhase::Change,
348            pointerinjector::EventPhase::Remove,
349        ];
350
351        // Make the trace duration end on the call to injector.inject, not the call's return.
352        // The duration should start before the flow_begin is minted in
353        // create_pointer_sample_event, and it should not include the injector.inject() call's
354        // return from await.
355        fuchsia_trace::duration_begin!(c"input", c"touch-inject-into-scenic");
356
357        let mut events: Vec<pointerinjector::Event> = vec![];
358        for phase in ordered_phases {
359            let contacts: Vec<touch_binding::TouchContact> = touch_event
360                .injector_contacts
361                .get(&phase)
362                .map_or(vec![], |contacts| contacts.to_owned());
363            let new_events = contacts.into_iter().map(|contact| {
364                Self::create_pointer_sample_event(
365                    phase,
366                    &contact,
367                    touch_descriptor,
368                    &self.display_size,
369                    event_time,
370                    touch_event.wake_lease.take(),
371                )
372            });
373            events.extend(new_events);
374        }
375
376        let injector =
377            self.mutable_state.borrow().injectors.get(&touch_descriptor.device_id).cloned();
378        if let Some(injector) = injector {
379            // This trace duration ends before calling inject_events.
380            fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
381            let _ = injector.inject_events(events);
382            Ok(())
383        } else {
384            fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
385            Err(anyhow::format_err!(
386                "No injector found for touch device {}.",
387                touch_descriptor.device_id
388            ))
389        }
390    }
391
392    /// Creates a [`fidl_fuchsia_ui_pointerinjector::Event`] representing the given touch contact.
393    ///
394    /// # Parameters
395    /// - `phase`: The phase of the touch contact.
396    /// - `contact`: The touch contact to create the event for.
397    /// - `touch_descriptor`: The device descriptor for the device that generated the event.
398    /// - `display_size`: The size of the associated touch display.
399    /// - `event_time`: The time in nanoseconds when the event was first recorded.
400    /// - `wake_lease`: The wake lease for this event.
401    fn create_pointer_sample_event(
402        phase: pointerinjector::EventPhase,
403        contact: &touch_binding::TouchContact,
404        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
405        display_size: &Size,
406        event_time: zx::MonotonicInstant,
407        wake_lease: Option<zx::EventPair>,
408    ) -> pointerinjector::Event {
409        let position =
410            Self::display_coordinate_from_contact(&contact, &touch_descriptor, display_size);
411        let pointer_sample = pointerinjector::PointerSample {
412            pointer_id: Some(contact.id),
413            phase: Some(phase),
414            position_in_viewport: Some([position.x, position.y]),
415            scroll_v: None,
416            scroll_h: None,
417            pressed_buttons: None,
418            ..Default::default()
419        };
420        let data = pointerinjector::Data::PointerSample(pointer_sample);
421
422        let trace_flow_id = fuchsia_trace::Id::random();
423        let event = pointerinjector::Event {
424            timestamp: Some(event_time.into_nanos()),
425            data: Some(data),
426            trace_flow_id: Some(trace_flow_id.into()),
427            wake_lease,
428            ..Default::default()
429        };
430
431        fuchsia_trace::flow_begin!(c"input", c"dispatch_event_to_scenic", trace_flow_id);
432
433        event
434    }
435
436    /// Converts an input event touch to a display coordinate, which is the coordinate space in
437    /// which Scenic handles events.
438    ///
439    /// The display coordinate is calculated by normalizing the contact position to the display
440    /// size. It does not account for the viewport position, which Scenic handles directly.
441    ///
442    /// # Parameters
443    /// - `contact`: The contact to get the display coordinate from.
444    /// - `touch_descriptor`: The device descriptor for the device that generated the event.
445    ///                       This is used to compute the device coordinate.
446    ///
447    /// # Returns
448    /// (x, y) coordinates.
449    fn display_coordinate_from_contact(
450        contact: &touch_binding::TouchContact,
451        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
452        display_size: &Size,
453    ) -> Position {
454        if let Some(contact_descriptor) = touch_descriptor.contacts.first() {
455            // Scale the x position.
456            let x_range: f32 =
457                contact_descriptor.x_range.max as f32 - contact_descriptor.x_range.min as f32;
458            let x_wrt_range: f32 = contact.position.x - contact_descriptor.x_range.min as f32;
459            let x: f32 = (display_size.width * x_wrt_range) / x_range;
460
461            // Scale the y position.
462            let y_range: f32 =
463                contact_descriptor.y_range.max as f32 - contact_descriptor.y_range.min as f32;
464            let y_wrt_range: f32 = contact.position.y - contact_descriptor.y_range.min as f32;
465            let y: f32 = (display_size.height * y_wrt_range) / y_range;
466
467            Position { x, y }
468        } else {
469            return contact.position;
470        }
471    }
472
473    /// Watches for viewport updates from the scene manager.
474    pub async fn watch_viewport(self: Rc<Self>) {
475        let configuration_proxy = self.configuration_proxy.clone();
476        let mut viewport_stream = HangingGetStream::new(
477            configuration_proxy,
478            pointerinjector_config::SetupProxy::watch_viewport,
479        );
480        loop {
481            match viewport_stream.next().await {
482                Some(Ok(new_viewport)) => {
483                    // Update the viewport tracked by this handler.
484                    self.mutable_state.borrow_mut().viewport = Some(new_viewport.clone());
485
486                    // Update Scenic with the latest viewport.
487                    let injectors: Vec<pointerinjector::DeviceProxy> =
488                        self.mutable_state.borrow_mut().injectors.values().cloned().collect();
489                    for injector in injectors {
490                        let events = vec![pointerinjector::Event {
491                            timestamp: Some(fuchsia_async::MonotonicInstant::now().into_nanos()),
492                            data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
493                            trace_flow_id: Some(fuchsia_trace::Id::random().into()),
494                            ..Default::default()
495                        }];
496                        injector.inject_events(events).expect("Failed to inject updated viewport.");
497                    }
498                }
499                Some(Err(e)) => {
500                    self.metrics_logger.log_error(
501                        InputPipelineErrorMetricDimensionEvent::TouchInjectorErrorWhileReadingViewportUpdate,
502                        std::format!("Error while reading viewport update: {}", e));
503                    return;
504                }
505                None => {
506                    self.metrics_logger.log_error(
507                        InputPipelineErrorMetricDimensionEvent::TouchInjectorViewportUpdateStreamTerminatedUnexpectedly,
508                        "Viewport update stream terminated unexpectedly");
509                    return;
510                }
511            }
512        }
513    }
514
515    /// Creates a fidl_ui_input::TouchButtonsEvent from a touch_binding::TouchScreenEvent.
516    ///
517    /// # Parameters
518    /// - `event`: The TouchScreenEvent to create a TouchButtonsEvent from.
519    /// - `event_time`: The time when the event was first recorded.
520    /// - `touch_descriptor`: The descriptor of the new touch device.
521    fn create_touch_buttons_event(
522        event: &mut touch_binding::TouchScreenEvent,
523        event_time: zx::MonotonicInstant,
524        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
525    ) -> fidl_ui_input::TouchButtonsEvent {
526        let pressed_buttons = match event.pressed_buttons.len() {
527            0 => None,
528            _ => Some(
529                event
530                    .pressed_buttons
531                    .clone()
532                    .into_iter()
533                    .map(|button| match button {
534                        fidl_input_report::TouchButton::Palm => fidl_ui_input::TouchButton::Palm,
535                        fidl_input_report::TouchButton::__SourceBreaking { unknown_ordinal: n } => {
536                            fidl_ui_input::TouchButton::__SourceBreaking {
537                                unknown_ordinal: n as u32,
538                            }
539                        }
540                    })
541                    .collect::<Vec<_>>(),
542            ),
543        };
544        fidl_ui_input::TouchButtonsEvent {
545            event_time: Some(event_time),
546            device_info: Some(fidl_ui_input::TouchDeviceInfo {
547                id: Some(touch_descriptor.device_id),
548                ..Default::default()
549            }),
550            pressed_buttons,
551            wake_lease: event.wake_lease.take(),
552            ..Default::default()
553        }
554    }
555
556    /// Sends touch button events to touch button listeners.
557    ///
558    /// # Parameters
559    /// - `event`: The event to send to the listeners.
560    async fn send_event_to_listeners(self: &Rc<Self>, event: &fidl_ui_input::TouchButtonsEvent) {
561        let tracker = &self.mutable_state.borrow().send_event_task_tracker;
562
563        for (handle, listener) in &self.mutable_state.borrow().listeners {
564            let weak_handler = Rc::downgrade(&self);
565            let listener_clone = listener.clone();
566            let handle_clone = handle.clone();
567            let event_to_send = Self::clone_event(event);
568            let fut = async move {
569                match listener_clone.on_event(event_to_send).await {
570                    Ok(_) => {}
571                    Err(e) => {
572                        if let Some(handler) = weak_handler.upgrade() {
573                            handler.mutable_state.borrow_mut().listeners.remove(&handle_clone);
574                            log::info!(
575                                "Unregistering listener; unable to send TouchButtonsEvent: {:?}",
576                                e
577                            )
578                        }
579                    }
580                }
581            };
582
583            let metrics_logger_clone = self.metrics_logger.clone();
584            tracker.track(metrics_logger_clone, fasync::Task::local(fut));
585        }
586    }
587
588    // Add the listener to the registry.
589    ///
590    /// # Parameters
591    /// - `proxy`: A new listener proxy to send events to.
592    pub async fn register_listener_proxy(
593        self: &Rc<Self>,
594        proxy: fidl_ui_policy::TouchButtonsListenerProxy,
595    ) {
596        self.mutable_state
597            .borrow_mut()
598            .listeners
599            .insert(proxy.as_channel().raw_handle(), proxy.clone());
600
601        // Send the listener the last touch button event.
602        if let Some(event) = &self.mutable_state.borrow().last_button_event {
603            let event_to_send = Self::clone_event(event);
604            let fut = async move {
605                match proxy.on_event(event_to_send).await {
606                    Ok(_) => {}
607                    Err(e) => {
608                        log::info!("Failed to send touch buttons event to listener {:?}", e)
609                    }
610                }
611            };
612            let metrics_logger_clone = self.metrics_logger.clone();
613            self.mutable_state
614                .borrow()
615                .send_event_task_tracker
616                .track(metrics_logger_clone, fasync::Task::local(fut));
617        }
618    }
619}
620
621/// Maintains a collection of pending local [`Task`]s, allowing them to be dropped (and cancelled)
622/// en masse.
623#[derive(Debug)]
624pub struct LocalTaskTracker {
625    sender: mpsc::UnboundedSender<fasync::Task<()>>,
626    _receiver_task: fasync::Task<()>,
627}
628
629impl LocalTaskTracker {
630    pub fn new() -> Self {
631        let (sender, receiver) = mpsc::unbounded();
632        let receiver_task = fasync::Task::local(async move {
633            // Drop the tasks as they are completed.
634            receiver.for_each_concurrent(None, |task: fasync::Task<()>| task).await
635        });
636
637        Self { sender, _receiver_task: receiver_task }
638    }
639
640    /// Submits a new task to track.
641    pub fn track(&self, metrics_logger: metrics::MetricsLogger, task: fasync::Task<()>) {
642        match self.sender.unbounded_send(task) {
643            Ok(_) => {}
644            // `Full` should never happen because this is unbounded.
645            // `Disconnected` might happen if the `Service` was dropped. However, it's not clear how
646            // to create such a race condition.
647            Err(e) => {
648                metrics_logger.log_error(
649                    InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
650                    std::format!("Unexpected {e:?} while pushing task"),
651                );
652            }
653        };
654    }
655}
656
657#[cfg(test)]
658mod tests {
659    use super::*;
660    use crate::input_handler::InputHandler;
661    use crate::testing_utilities::{
662        create_fake_input_event, create_touch_contact, create_touch_pointer_sample_event,
663        create_touch_screen_event, create_touch_screen_event_with_handled, create_touchpad_event,
664        get_touch_screen_device_descriptor,
665    };
666    use assert_matches::assert_matches;
667    use futures::{FutureExt, TryStreamExt};
668    use maplit::hashmap;
669    use pretty_assertions::assert_eq;
670    use std::collections::HashSet;
671    use std::convert::TryFrom as _;
672    use std::ops::Add;
673    use {
674        fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
675        fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
676    };
677
678    const TOUCH_ID: u32 = 1;
679    const DISPLAY_WIDTH: f32 = 100.0;
680    const DISPLAY_HEIGHT: f32 = 100.0;
681
682    struct TestFixtures {
683        touch_handler: Rc<TouchInjectorHandler>,
684        device_listener_proxy: fidl_ui_policy::DeviceListenerRegistryProxy,
685        injector_registry_request_stream: pointerinjector::RegistryRequestStream,
686        configuration_request_stream: pointerinjector_config::SetupRequestStream,
687        inspector: fuchsia_inspect::Inspector,
688        _test_node: fuchsia_inspect::Node,
689    }
690
691    fn spawn_device_listener_registry_server(
692        handler: Rc<TouchInjectorHandler>,
693    ) -> fidl_ui_policy::DeviceListenerRegistryProxy {
694        let (device_listener_proxy, mut device_listener_stream) =
695            fidl::endpoints::create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>(
696            );
697
698        fasync::Task::local(async move {
699            loop {
700                match device_listener_stream.try_next().await {
701                    Ok(Some(
702                        fidl_ui_policy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
703                            listener,
704                            responder,
705                        },
706                    )) => {
707                        handler.register_listener_proxy(listener.into_proxy()).await;
708                        let _ = responder.send();
709                    }
710                    Ok(Some(_)) => {
711                        panic!("Unexpected registration");
712                    }
713                    Ok(None) => {
714                        break;
715                    }
716                    Err(e) => {
717                        panic!("Error handling device listener registry request stream: {}", e);
718                    }
719                }
720            }
721        })
722        .detach();
723
724        device_listener_proxy
725    }
726
727    impl TestFixtures {
728        async fn new() -> Self {
729            let inspector = fuchsia_inspect::Inspector::default();
730            let test_node = inspector.root().create_child("test_node");
731            let (configuration_proxy, mut configuration_request_stream) =
732                fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
733            let (injector_registry_proxy, injector_registry_request_stream) =
734                fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
735
736            let touch_handler_fut = TouchInjectorHandler::new_handler(
737                configuration_proxy,
738                injector_registry_proxy,
739                Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
740                &test_node,
741                metrics::MetricsLogger::default(),
742            );
743
744            let handle_initial_request_fut = async {
745                match configuration_request_stream.next().await {
746                    Some(Ok(pointerinjector_config::SetupRequest::GetViewRefs {
747                        responder,
748                        ..
749                    })) => {
750                        let context = fuchsia_scenic::ViewRefPair::new()
751                            .expect("Failed to create viewrefpair.")
752                            .view_ref;
753                        let target = fuchsia_scenic::ViewRefPair::new()
754                            .expect("Failed to create viewrefpair.")
755                            .view_ref;
756                        let _ = responder.send(context, target);
757                    }
758                    other => panic!("Expected GetViewRefs request, got {:?}", other),
759                }
760            };
761
762            let (touch_handler_res, _) =
763                futures::future::join(touch_handler_fut, handle_initial_request_fut).await;
764
765            let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
766            let device_listener_proxy =
767                spawn_device_listener_registry_server(touch_handler.clone());
768
769            TestFixtures {
770                touch_handler,
771                device_listener_proxy,
772                injector_registry_request_stream,
773                configuration_request_stream,
774                inspector,
775                _test_node: test_node,
776            }
777        }
778    }
779
780    /// Returns an |input_device::InputDeviceDescriptor::Touchpad|.
781    fn get_touchpad_device_descriptor() -> input_device::InputDeviceDescriptor {
782        input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
783            device_id: 1,
784            contacts: vec![touch_binding::ContactDeviceDescriptor {
785                x_range: fidl_input_report::Range { min: 0, max: 100 },
786                y_range: fidl_input_report::Range { min: 0, max: 100 },
787                x_unit: fidl_input_report::Unit {
788                    type_: fidl_input_report::UnitType::Meters,
789                    exponent: -6,
790                },
791                y_unit: fidl_input_report::Unit {
792                    type_: fidl_input_report::UnitType::Meters,
793                    exponent: -6,
794                },
795                pressure_range: None,
796                width_range: None,
797                height_range: None,
798            }],
799        })
800    }
801
802    /// Handles |fidl_fuchsia_pointerinjector::DeviceRequest|s by asserting the `injector_stream`
803    /// gets `expected_event`.
804    async fn handle_device_request_stream(
805        mut injector_stream: pointerinjector::DeviceRequestStream,
806        expected_event: pointerinjector::Event,
807    ) {
808        match injector_stream.next().await {
809            Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
810                panic!("DeviceRequest::Inject is deprecated.");
811            }
812            Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
813                assert_eq!(events.len(), 1);
814                assert_eq!(events[0].timestamp, expected_event.timestamp);
815                assert_eq!(events[0].data, expected_event.data);
816            }
817            Some(Err(e)) => panic!("FIDL error {}", e),
818            None => panic!("Expected another event."),
819        }
820    }
821
822    // Creates a |pointerinjector::Viewport|.
823    fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
824        pointerinjector::Viewport {
825            extents: Some([[min, min], [max, max]]),
826            viewport_to_context_transform: None,
827            ..Default::default()
828        }
829    }
830
831    #[fuchsia::test]
832    async fn events_with_pressed_buttons_are_sent_to_listener() {
833        let fixtures = TestFixtures::new().await;
834        let (listener, mut listener_stream) =
835            fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
836        fixtures
837            .device_listener_proxy
838            .register_touch_buttons_listener(listener)
839            .await
840            .expect("Failed to register listener.");
841
842        let descriptor = get_touch_screen_device_descriptor();
843        let event_time = zx::MonotonicInstant::get();
844        let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
845
846        let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
847
848        let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
849            event_time: Some(event_time),
850            device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
851            ..Default::default()
852        };
853
854        assert_matches!(
855            listener_stream.next().await,
856            Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
857                event,
858                responder,
859            })) => {
860                assert_eq!(event, expected_touch_buttons_event);
861                let _ = responder.send();
862            }
863        );
864    }
865
866    #[fuchsia::test]
867    async fn events_with_contacts_are_not_sent_to_listener() {
868        let fixtures = TestFixtures::new().await;
869        let (listener, mut listener_stream) =
870            fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
871        fixtures
872            .device_listener_proxy
873            .register_touch_buttons_listener(listener)
874            .await
875            .expect("Failed to register listener.");
876
877        let descriptor = get_touch_screen_device_descriptor();
878        let event_time = zx::MonotonicInstant::get();
879        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
880        let input_event = create_touch_screen_event(
881            hashmap! {
882                fidl_ui_input::PointerEventPhase::Add
883                    => vec![contact.clone()],
884            },
885            event_time,
886            &descriptor,
887        );
888
889        let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
890
891        assert!(listener_stream.next().now_or_never().is_none());
892    }
893
894    #[fuchsia::test]
895    async fn multiple_listeners_receive_pressed_button_events() {
896        let fixtures = TestFixtures::new().await;
897        let (first_listener, mut first_listener_stream) =
898            fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
899        let (second_listener, mut second_listener_stream) =
900            fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
901        fixtures
902            .device_listener_proxy
903            .register_touch_buttons_listener(first_listener)
904            .await
905            .expect("Failed to register listener.");
906        fixtures
907            .device_listener_proxy
908            .register_touch_buttons_listener(second_listener)
909            .await
910            .expect("Failed to register listener.");
911
912        let descriptor = get_touch_screen_device_descriptor();
913        let event_time = zx::MonotonicInstant::get();
914        let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
915
916        let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
917
918        let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
919            event_time: Some(event_time),
920            device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
921            ..Default::default()
922        };
923
924        assert_matches!(
925            first_listener_stream.next().await,
926            Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
927                event,
928                responder,
929            })) => {
930                assert_eq!(event, expected_touch_buttons_event);
931                let _ = responder.send();
932            }
933        );
934        assert_matches!(
935            second_listener_stream.next().await,
936            Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
937                event,
938                responder,
939            })) => {
940                assert_eq!(event, expected_touch_buttons_event);
941                let _ = responder.send();
942            }
943        );
944    }
945
946    // Tests that TouchInjectorHandler::watch_viewport() tracks viewport updates and notifies
947    // injectors about said updates.
948    #[fuchsia::test]
949    async fn receives_viewport_updates() {
950        let mut fixtures = TestFixtures::new().await;
951
952        // Add an injector.
953        let (injector_device_proxy, mut injector_device_request_stream) =
954            fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
955        fixtures
956            .touch_handler
957            .mutable_state
958            .borrow_mut()
959            .injectors
960            .insert(1, injector_device_proxy);
961
962        // This nested block is used to bound the lifetime of `watch_viewport_fut`.
963        {
964            // Request a viewport update.
965            let _watch_viewport_task =
966                fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
967
968            // Send a viewport update.
969            match fixtures.configuration_request_stream.next().await {
970                Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
971                    responder, ..
972                })) => {
973                    responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
974                }
975                other => panic!("Received unexpected value: {:?}", other),
976            };
977
978            // Check that the injector received an updated viewport
979            match injector_device_request_stream.next().await {
980                Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
981                    panic!("DeviceRequest::Inject is deprecated.");
982                }
983                Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
984                    assert_eq!(events.len(), 1);
985                    assert!(events[0].data.is_some());
986                    assert_eq!(
987                        events[0].data,
988                        Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
989                    );
990                }
991                other => panic!("Received unexpected value: {:?}", other),
992            }
993
994            // Request viewport update.
995            // Send viewport update.
996            match fixtures.configuration_request_stream.next().await {
997                Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
998                    responder, ..
999                })) => {
1000                    responder
1001                        .send(&create_viewport(100.0, 200.0))
1002                        .expect("Failed to send viewport.");
1003                }
1004                other => panic!("Received unexpected value: {:?}", other),
1005            };
1006
1007            // Check that the injector received an updated viewport
1008            match injector_device_request_stream.next().await {
1009                Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1010                    panic!("DeviceRequest::Inject is deprecated.");
1011                }
1012                Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1013                    assert_eq!(events.len(), 1);
1014                    assert!(events[0].data.is_some());
1015                    assert_eq!(
1016                        events[0].data,
1017                        Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
1018                    );
1019                }
1020                other => panic!("Received unexpected value: {:?}", other),
1021            }
1022        }
1023
1024        // Check the viewport on the handler is accurate.
1025        let expected_viewport = create_viewport(100.0, 200.0);
1026        assert_eq!(fixtures.touch_handler.mutable_state.borrow().viewport, Some(expected_viewport));
1027    }
1028
1029    // Tests that an add contact event is dropped without a viewport.
1030    #[fuchsia::test]
1031    async fn add_contact_drops_without_viewport() {
1032        let mut fixtures = TestFixtures::new().await;
1033
1034        // Create touch event.
1035        let event_time = zx::MonotonicInstant::get();
1036        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1037        let descriptor = get_touch_screen_device_descriptor();
1038        let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1039            hashmap! {
1040                fidl_ui_input::PointerEventPhase::Add
1041                    => vec![contact.clone()],
1042            },
1043            event_time,
1044            &descriptor,
1045        ))
1046        .unwrap();
1047
1048        // Clear the viewport that was set during test fixture setup.
1049        fixtures.touch_handler.mutable_state.borrow_mut().viewport = None;
1050
1051        // Try to handle the event.
1052        let _ = fixtures.touch_handler.clone().handle_unhandled_input_event(input_event).await;
1053
1054        // Injector should not receive anything because the handler has no viewport.
1055        assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1056    }
1057
1058    // Tests that an add contact event is handled correctly with a viewport.
1059    #[fuchsia::test]
1060    async fn add_contact_succeeds_with_viewport() {
1061        let mut fixtures = TestFixtures::new().await;
1062
1063        // Add an injector.
1064        let (injector_device_proxy, mut injector_device_request_stream) =
1065            fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1066        fixtures
1067            .touch_handler
1068            .mutable_state
1069            .borrow_mut()
1070            .injectors
1071            .insert(1, injector_device_proxy);
1072
1073        // Request a viewport update.
1074        let _watch_viewport_task =
1075            fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1076
1077        // Send a viewport update.
1078        match fixtures.configuration_request_stream.next().await {
1079            Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1080                responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1081            }
1082            other => panic!("Received unexpected value: {:?}", other),
1083        };
1084
1085        // Check that the injector received an updated viewport
1086        match injector_device_request_stream.next().await {
1087            Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1088                panic!("DeviceRequest::Inject is deprecated.");
1089            }
1090            Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1091                assert_eq!(events.len(), 1);
1092                assert!(events[0].data.is_some());
1093                assert_eq!(
1094                    events[0].data,
1095                    Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1096                );
1097            }
1098            other => panic!("Received unexpected value: {:?}", other),
1099        }
1100
1101        // Create touch event.
1102        let event_time = zx::MonotonicInstant::get();
1103        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1104        let descriptor = get_touch_screen_device_descriptor();
1105        let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1106            hashmap! {
1107                fidl_ui_input::PointerEventPhase::Add
1108                    => vec![contact.clone()],
1109            },
1110            event_time,
1111            &descriptor,
1112        ))
1113        .unwrap();
1114
1115        // Handle event.
1116        let handle_event_fut =
1117            fixtures.touch_handler.clone().handle_unhandled_input_event(input_event);
1118
1119        // Declare expected event.
1120        let expected_event = create_touch_pointer_sample_event(
1121            pointerinjector::EventPhase::Add,
1122            &contact,
1123            Position { x: 20.0, y: 40.0 },
1124            event_time,
1125        );
1126
1127        // Await all futures concurrently. If this completes, then the touch event was handled and
1128        // matches `expected_event`.
1129        let device_fut =
1130            handle_device_request_stream(injector_device_request_stream, expected_event);
1131        let (handle_result, _) = futures::future::join(handle_event_fut, device_fut).await;
1132
1133        // No unhandled events.
1134        assert_matches!(
1135            handle_result.as_slice(),
1136            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1137        );
1138    }
1139
1140    // Tests that an add touchpad contact event with viewport is unhandled and not send to scenic.
1141    #[fuchsia::test]
1142    async fn add_touchpad_contact_with_viewport() {
1143        let mut fixtures = TestFixtures::new().await;
1144
1145        // Add an injector.
1146        let (injector_device_proxy, mut injector_device_request_stream) =
1147            fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1148        fixtures
1149            .touch_handler
1150            .mutable_state
1151            .borrow_mut()
1152            .injectors
1153            .insert(1, injector_device_proxy);
1154
1155        // Request a viewport update.
1156        let _watch_viewport_task =
1157            fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1158
1159        // Send a viewport update.
1160        match fixtures.configuration_request_stream.next().await {
1161            Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1162                responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1163            }
1164            other => panic!("Received unexpected value: {:?}", other),
1165        };
1166
1167        // Check that the injector received an updated viewport
1168        match injector_device_request_stream.next().await {
1169            Some(Ok(pointerinjector::DeviceRequest::Inject { .. })) => {
1170                panic!("DeviceRequest::Inject is deprecated.");
1171            }
1172            Some(Ok(pointerinjector::DeviceRequest::InjectEvents { events, .. })) => {
1173                assert_eq!(events.len(), 1);
1174                assert!(events[0].data.is_some());
1175                assert_eq!(
1176                    events[0].data,
1177                    Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1178                );
1179            }
1180            other => panic!("Received unexpected value: {:?}", other),
1181        }
1182
1183        // Create touch event.
1184        let event_time = zx::MonotonicInstant::get();
1185        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1186        let descriptor = get_touchpad_device_descriptor();
1187        let input_event = input_device::UnhandledInputEvent::try_from(create_touchpad_event(
1188            vec![contact.clone()],
1189            HashSet::new(),
1190            event_time,
1191            &descriptor,
1192        ))
1193        .unwrap();
1194
1195        // Handle event.
1196        let handle_event_fut =
1197            fixtures.touch_handler.clone().handle_unhandled_input_event(input_event);
1198
1199        let handle_result = handle_event_fut.await;
1200
1201        // Event is not handled.
1202        assert_matches!(
1203            handle_result.as_slice(),
1204            [input_device::InputEvent { handled: input_device::Handled::No, .. }]
1205        );
1206
1207        // Injector should not receive anything because the handler does not support touchpad yet.
1208        assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1209    }
1210
1211    #[fuchsia::test(allow_stalls = false)]
1212    async fn touch_injector_handler_initialized_with_inspect_node() {
1213        let fixtures = TestFixtures::new().await;
1214        diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1215            test_node: {
1216                touch_injector_handler: {
1217                    events_received_count: 0u64,
1218                    events_handled_count: 0u64,
1219                    last_received_timestamp_ns: 0u64,
1220                    "fuchsia.inspect.Health": {
1221                        status: "STARTING_UP",
1222                        // Timestamp value is unpredictable and not relevant in this context,
1223                        // so we only assert that the property is present.
1224                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
1225                    },
1226                }
1227            }
1228        });
1229    }
1230
1231    #[fuchsia::test(allow_stalls = false)]
1232    async fn touch_injector_handler_inspect_counts_events() {
1233        let fixtures = TestFixtures::new().await;
1234
1235        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1236        let descriptor = get_touch_screen_device_descriptor();
1237        let event_time1 = zx::MonotonicInstant::get();
1238        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1239        let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
1240
1241        let input_events = vec![
1242            create_touch_screen_event(
1243                hashmap! {
1244                    fidl_ui_input::PointerEventPhase::Add
1245                        => vec![contact.clone()],
1246                },
1247                event_time1,
1248                &descriptor,
1249            ),
1250            create_touch_screen_event(
1251                hashmap! {
1252                    fidl_ui_input::PointerEventPhase::Move
1253                        => vec![contact.clone()],
1254                },
1255                event_time2,
1256                &descriptor,
1257            ),
1258            // Should not count non-touch input event.
1259            create_fake_input_event(event_time2),
1260            // Should not count received event that has already been handled.
1261            create_touch_screen_event_with_handled(
1262                hashmap! {
1263                    fidl_ui_input::PointerEventPhase::Move
1264                        => vec![contact.clone()],
1265                },
1266                event_time2,
1267                &descriptor,
1268                input_device::Handled::Yes,
1269            ),
1270            create_touch_screen_event(
1271                hashmap! {
1272                    fidl_ui_input::PointerEventPhase::Remove
1273                        => vec![contact.clone()],
1274                },
1275                event_time3,
1276                &descriptor,
1277            ),
1278        ];
1279
1280        for input_event in input_events {
1281            fixtures.touch_handler.clone().handle_input_event(input_event).await;
1282        }
1283
1284        let last_received_event_time: u64 = event_time3.into_nanos().try_into().unwrap();
1285
1286        diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1287            test_node: {
1288                touch_injector_handler: {
1289                    events_received_count: 3u64,
1290                    events_handled_count: 3u64,
1291                    last_received_timestamp_ns: last_received_event_time,
1292                    "fuchsia.inspect.Health": {
1293                        status: "STARTING_UP",
1294                        // Timestamp value is unpredictable and not relevant in this context,
1295                        // so we only assert that the property is present.
1296                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
1297                    },
1298                }
1299            }
1300        });
1301    }
1302
1303    #[fuchsia::test]
1304    async fn clone_event_with_lease_duplicates_lease() {
1305        let (event_pair, _) = fidl::EventPair::create();
1306        let event = fidl_ui_input::TouchButtonsEvent {
1307            event_time: Some(zx::MonotonicInstant::from_nanos(1)),
1308            device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
1309            pressed_buttons: Some(vec![fidl_ui_input::TouchButton::Palm]),
1310            wake_lease: Some(event_pair),
1311            ..Default::default()
1312        };
1313        let cloned_event = TouchInjectorHandler::clone_event(&event);
1314        assert_eq!(event.event_time, cloned_event.event_time);
1315        assert_eq!(event.device_info, cloned_event.device_info);
1316        assert_eq!(event.pressed_buttons, cloned_event.pressed_buttons);
1317        assert!(event.wake_lease.is_some());
1318        assert!(cloned_event.wake_lease.is_some());
1319        assert_ne!(
1320            event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle(),
1321            cloned_event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle()
1322        );
1323    }
1324
1325    #[fuchsia::test]
1326    async fn clone_event_without_lease_has_no_lease() {
1327        let event = fidl_ui_input::TouchButtonsEvent {
1328            event_time: Some(zx::MonotonicInstant::from_nanos(1)),
1329            device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
1330            pressed_buttons: Some(vec![fidl_ui_input::TouchButton::Palm]),
1331            wake_lease: None,
1332            ..Default::default()
1333        };
1334        let cloned_event = TouchInjectorHandler::clone_event(&event);
1335        assert_eq!(event.event_time, cloned_event.event_time);
1336        assert_eq!(event.device_info, cloned_event.device_info);
1337        assert_eq!(event.pressed_buttons, cloned_event.pressed_buttons);
1338        assert!(event.wake_lease.is_none());
1339        assert!(cloned_event.wake_lease.is_none());
1340    }
1341}