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