Skip to main content

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