Skip to main content

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