Skip to main content

input_pipeline/
touch_injector_handler.rs

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