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