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