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