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