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