input_pipeline/
touch_injector_handler.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![warn(clippy::await_holding_refcell_ref)]
6use crate::input_handler::{InputHandlerStatus, UnhandledInputHandler};
7use crate::utils::{Position, Size};
8use crate::{input_device, metrics, touch_binding};
9use anyhow::{Context, Error, Result};
10use async_trait::async_trait;
11use async_utils::hanging_get::client::HangingGetStream;
12use fidl::endpoints::create_proxy;
13use fuchsia_component::client::connect_to_protocol;
14use fuchsia_inspect::health::Reporter;
15use futures::stream::StreamExt;
16use metrics_registry::*;
17use std::cell::RefCell;
18use std::collections::HashMap;
19use std::rc::Rc;
20use {
21    fidl_fuchsia_ui_pointerinjector as pointerinjector,
22    fidl_fuchsia_ui_pointerinjector_configuration as pointerinjector_config,
23};
24
25/// An input handler that parses touch events and forwards them to Scenic through the
26/// fidl_fuchsia_pointerinjector protocols.
27pub struct TouchInjectorHandler {
28    /// The mutable fields of this handler.
29    mutable_state: RefCell<MutableState>,
30
31    /// The scope and coordinate system of injection.
32    /// See fidl_fuchsia_pointerinjector::Context for more details.
33    context_view_ref: fidl_fuchsia_ui_views::ViewRef,
34
35    /// The region where dispatch is attempted for injected events.
36    /// See fidl_fuchsia_pointerinjector::Target for more details.
37    target_view_ref: fidl_fuchsia_ui_views::ViewRef,
38
39    /// The size of the display associated with the touch device, used to convert
40    /// coordinates from the touch input report to device coordinates (which is what
41    /// Scenic expects).
42    display_size: Size,
43
44    /// The FIDL proxy to register new injectors.
45    injector_registry_proxy: pointerinjector::RegistryProxy,
46
47    /// The FIDL proxy used to get configuration details for pointer injection.
48    configuration_proxy: pointerinjector_config::SetupProxy,
49
50    /// The inventory of this handler's Inspect status.
51    pub inspect_status: InputHandlerStatus,
52
53    /// The metrics logger.
54    metrics_logger: metrics::MetricsLogger,
55}
56
57#[derive(Debug)]
58struct MutableState {
59    /// A rectangular region that directs injected events into a target.
60    /// See fidl_fuchsia_pointerinjector::Viewport for more details.
61    viewport: Option<pointerinjector::Viewport>,
62
63    /// The injectors registered with Scenic, indexed by their device ids.
64    injectors: HashMap<u32, pointerinjector::DeviceProxy>,
65}
66
67#[async_trait(?Send)]
68impl UnhandledInputHandler for TouchInjectorHandler {
69    async fn handle_unhandled_input_event(
70        self: Rc<Self>,
71        unhandled_input_event: input_device::UnhandledInputEvent,
72    ) -> Vec<input_device::InputEvent> {
73        fuchsia_trace::duration!(c"input", c"presentation_on_event");
74        match unhandled_input_event {
75            input_device::UnhandledInputEvent {
76                device_event: input_device::InputDeviceEvent::TouchScreen(ref touch_event),
77                device_descriptor:
78                    input_device::InputDeviceDescriptor::TouchScreen(ref touch_device_descriptor),
79                event_time,
80                trace_id,
81            } => {
82                self.inspect_status.count_received_event(input_device::InputEvent::from(
83                    unhandled_input_event.clone(),
84                ));
85                fuchsia_trace::flow_end!(
86                    c"input",
87                    c"report-to-event",
88                    trace_id.unwrap_or_else(|| 0.into())
89                );
90                // Create a new injector if this is the first time seeing device_id.
91                if let Err(e) = self.ensure_injector_registered(&touch_device_descriptor).await {
92                    self.metrics_logger.log_error(
93                        InputPipelineErrorMetricDimensionEvent::TouchInjectorEnsureInjectorRegisteredFailed,
94                        std::format!("ensure_injector_registered failed: {}", e));
95                }
96
97                // Handle the event.
98                if let Err(e) = self
99                    .send_event_to_scenic(&touch_event, &touch_device_descriptor, event_time)
100                    .await
101                {
102                    self.metrics_logger.log_error(
103                        InputPipelineErrorMetricDimensionEvent::TouchInjectorSendEventToScenicFailed,
104                        std::format!("send_event_to_scenic failed: {}", e));
105                }
106
107                // Consume the input event.
108                self.inspect_status.count_handled_event();
109                vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
110            }
111            _ => vec![input_device::InputEvent::from(unhandled_input_event)],
112        }
113    }
114
115    fn set_handler_healthy(self: std::rc::Rc<Self>) {
116        self.inspect_status.health_node.borrow_mut().set_ok();
117    }
118
119    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
120        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
121    }
122}
123
124impl TouchInjectorHandler {
125    /// Creates a new touch handler that holds touch pointer injectors.
126    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
127    /// Example:
128    /// let handler = TouchInjectorHandler::new(display_size).await?;
129    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
130    ///
131    /// # Parameters
132    /// - `display_size`: The size of the associated touch display.
133    ///
134    /// # Errors
135    /// If unable to connect to pointerinjector protocols.
136    pub async fn new(
137        display_size: Size,
138        input_handlers_node: &fuchsia_inspect::Node,
139        metrics_logger: metrics::MetricsLogger,
140    ) -> Result<Rc<Self>, Error> {
141        let configuration_proxy = connect_to_protocol::<pointerinjector_config::SetupMarker>()?;
142        let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
143
144        Self::new_handler(
145            configuration_proxy,
146            injector_registry_proxy,
147            display_size,
148            input_handlers_node,
149            metrics_logger,
150        )
151        .await
152    }
153
154    /// Creates a new touch handler that holds touch pointer injectors.
155    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
156    /// Example:
157    /// let handler = TouchInjectorHandler::new_with_config_proxy(config_proxy, display_size).await?;
158    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
159    ///
160    /// # Parameters
161    /// - `configuration_proxy`: A proxy used to get configuration details for pointer
162    ///    injection.
163    /// - `display_size`: The size of the associated touch display.
164    ///
165    /// # Errors
166    /// If unable to get injection view refs from `configuration_proxy`.
167    /// If unable to connect to pointerinjector Registry protocol.
168    pub async fn new_with_config_proxy(
169        configuration_proxy: pointerinjector_config::SetupProxy,
170        display_size: Size,
171        input_handlers_node: &fuchsia_inspect::Node,
172        metrics_logger: metrics::MetricsLogger,
173    ) -> Result<Rc<Self>, Error> {
174        let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
175        Self::new_handler(
176            configuration_proxy,
177            injector_registry_proxy,
178            display_size,
179            input_handlers_node,
180            metrics_logger,
181        )
182        .await
183    }
184
185    /// Creates a new touch handler that holds touch pointer injectors.
186    /// The caller is expected to spawn a task to continually watch for updates to the viewport.
187    /// Example:
188    /// let handler = TouchInjectorHandler::new_handler(None, None, display_size).await?;
189    /// fasync::Task::local(handler.clone().watch_viewport()).detach();
190    ///
191    /// # Parameters
192    /// - `configuration_proxy`: A proxy used to get configuration details for pointer
193    ///    injection.
194    /// - `injector_registry_proxy`: A proxy used to register new pointer injectors.  If
195    ///    none is provided, connect to protocol routed to this component.
196    /// - `display_size`: The size of the associated touch display.
197    ///
198    /// # Errors
199    /// If unable to get injection view refs from `configuration_proxy`.
200    async fn new_handler(
201        configuration_proxy: pointerinjector_config::SetupProxy,
202        injector_registry_proxy: pointerinjector::RegistryProxy,
203        display_size: Size,
204        input_handlers_node: &fuchsia_inspect::Node,
205        metrics_logger: metrics::MetricsLogger,
206    ) -> Result<Rc<Self>, Error> {
207        // Get the context and target views to inject into.
208        let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
209
210        let inspect_status = InputHandlerStatus::new(
211            input_handlers_node,
212            "touch_injector_handler",
213            /* generates_events */ false,
214        );
215        let handler = Rc::new(Self {
216            mutable_state: RefCell::new(MutableState { viewport: None, injectors: HashMap::new() }),
217            context_view_ref,
218            target_view_ref,
219            display_size,
220            injector_registry_proxy,
221            configuration_proxy,
222            inspect_status,
223            metrics_logger,
224        });
225
226        Ok(handler)
227    }
228
229    /// Adds a new pointer injector and tracks it in `self.injectors` if one doesn't exist at
230    /// `touch_descriptor.device_id`.
231    ///
232    /// # Parameters
233    /// - `touch_descriptor`: The descriptor of the new touch device.
234    async fn ensure_injector_registered(
235        self: &Rc<Self>,
236        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
237    ) -> Result<(), anyhow::Error> {
238        if self.mutable_state.borrow().injectors.contains_key(&touch_descriptor.device_id) {
239            return Ok(());
240        }
241
242        // Create a new injector.
243        let (device_proxy, device_server) = create_proxy::<pointerinjector::DeviceMarker>();
244        let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
245            .context("Failed to duplicate context view ref.")?;
246        let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
247            .context("Failed to duplicate target view ref.")?;
248        let viewport = self.mutable_state.borrow().viewport.clone();
249        if viewport.is_none() {
250            // An injector without a viewport is not valid. The event will be dropped
251            // since the handler will not have a registered injector to inject into.
252            return Err(anyhow::format_err!(
253                "Received a touch event without a viewport to inject into."
254            ));
255        }
256        let config = pointerinjector::Config {
257            device_id: Some(touch_descriptor.device_id),
258            device_type: Some(pointerinjector::DeviceType::Touch),
259            context: Some(pointerinjector::Context::View(context)),
260            target: Some(pointerinjector::Target::View(target)),
261            viewport,
262            dispatch_policy: Some(pointerinjector::DispatchPolicy::TopHitAndAncestorsInTarget),
263            scroll_v_range: None,
264            scroll_h_range: None,
265            buttons: None,
266            ..Default::default()
267        };
268
269        // Keep track of the injector.
270        self.mutable_state.borrow_mut().injectors.insert(touch_descriptor.device_id, device_proxy);
271
272        // Register the new injector.
273        self.injector_registry_proxy
274            .register(config, device_server)
275            .await
276            .context("Failed to register injector.")?;
277        log::info!("Registered injector with device id {:?}", touch_descriptor.device_id);
278
279        Ok(())
280    }
281
282    /// Sends the given event to Scenic.
283    ///
284    /// # Parameters
285    /// - `touch_event`: The touch event to send to Scenic.
286    /// - `touch_descriptor`: The descriptor for the device that sent the touch event.
287    /// - `event_time`: The time in nanoseconds when the event was first recorded.
288    async fn send_event_to_scenic(
289        &self,
290        touch_event: &touch_binding::TouchScreenEvent,
291        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
292        event_time: zx::MonotonicInstant,
293    ) -> Result<(), anyhow::Error> {
294        // The order in which events are sent to clients.
295        let ordered_phases = vec![
296            pointerinjector::EventPhase::Add,
297            pointerinjector::EventPhase::Change,
298            pointerinjector::EventPhase::Remove,
299        ];
300
301        // Make the trace duration end on the call to injector.inject, not the call's return.
302        // The duration should start before the flow_begin is minted in
303        // create_pointer_sample_event, and it should not include the injector.inject() call's
304        // return from await.
305        fuchsia_trace::duration_begin!(c"input", c"touch-inject-into-scenic");
306
307        let mut events: Vec<pointerinjector::Event> = vec![];
308        for phase in ordered_phases {
309            let contacts: Vec<touch_binding::TouchContact> = touch_event
310                .injector_contacts
311                .get(&phase)
312                .map_or(vec![], |contacts| contacts.to_owned());
313            let new_events = contacts.into_iter().map(|contact| {
314                Self::create_pointer_sample_event(
315                    phase,
316                    &contact,
317                    touch_descriptor,
318                    &self.display_size,
319                    event_time,
320                )
321            });
322            events.extend(new_events);
323        }
324
325        let injector =
326            self.mutable_state.borrow().injectors.get(&touch_descriptor.device_id).cloned();
327        if let Some(injector) = injector {
328            let fut = injector.inject(&events);
329            // This trace duration ends before awaiting on the returned future.
330            fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
331            let _ = fut.await;
332            Ok(())
333        } else {
334            fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
335            Err(anyhow::format_err!(
336                "No injector found for touch device {}.",
337                touch_descriptor.device_id
338            ))
339        }
340    }
341
342    /// Creates a [`fidl_fuchsia_ui_pointerinjector::Event`] representing the given touch contact.
343    ///
344    /// # Parameters
345    /// - `phase`: The phase of the touch contact.
346    /// - `contact`: The touch contact to create the event for.
347    /// - `touch_descriptor`: The device descriptor for the device that generated the event.
348    /// - `display_size`: The size of the associated touch display.
349    /// - `event_time`: The time in nanoseconds when the event was first recorded.
350    fn create_pointer_sample_event(
351        phase: pointerinjector::EventPhase,
352        contact: &touch_binding::TouchContact,
353        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
354        display_size: &Size,
355        event_time: zx::MonotonicInstant,
356    ) -> pointerinjector::Event {
357        let position =
358            Self::display_coordinate_from_contact(&contact, &touch_descriptor, display_size);
359        let pointer_sample = pointerinjector::PointerSample {
360            pointer_id: Some(contact.id),
361            phase: Some(phase),
362            position_in_viewport: Some([position.x, position.y]),
363            scroll_v: None,
364            scroll_h: None,
365            pressed_buttons: None,
366            ..Default::default()
367        };
368        let data = pointerinjector::Data::PointerSample(pointer_sample);
369
370        let trace_flow_id = fuchsia_trace::Id::new();
371        let event = pointerinjector::Event {
372            timestamp: Some(event_time.into_nanos()),
373            data: Some(data),
374            trace_flow_id: Some(trace_flow_id.into()),
375            ..Default::default()
376        };
377
378        fuchsia_trace::flow_begin!(c"input", c"dispatch_event_to_scenic", trace_flow_id);
379
380        event
381    }
382
383    /// Converts an input event touch to a display coordinate, which is the coordinate space in
384    /// which Scenic handles events.
385    ///
386    /// The display coordinate is calculated by normalizing the contact position to the display
387    /// size. It does not account for the viewport position, which Scenic handles directly.
388    ///
389    /// # Parameters
390    /// - `contact`: The contact to get the display coordinate from.
391    /// - `touch_descriptor`: The device descriptor for the device that generated the event.
392    ///                       This is used to compute the device coordinate.
393    ///
394    /// # Returns
395    /// (x, y) coordinates.
396    fn display_coordinate_from_contact(
397        contact: &touch_binding::TouchContact,
398        touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
399        display_size: &Size,
400    ) -> Position {
401        if let Some(contact_descriptor) = touch_descriptor.contacts.first() {
402            // Scale the x position.
403            let x_range: f32 =
404                contact_descriptor.x_range.max as f32 - contact_descriptor.x_range.min as f32;
405            let x_wrt_range: f32 = contact.position.x - contact_descriptor.x_range.min as f32;
406            let x: f32 = (display_size.width * x_wrt_range) / x_range;
407
408            // Scale the y position.
409            let y_range: f32 =
410                contact_descriptor.y_range.max as f32 - contact_descriptor.y_range.min as f32;
411            let y_wrt_range: f32 = contact.position.y - contact_descriptor.y_range.min as f32;
412            let y: f32 = (display_size.height * y_wrt_range) / y_range;
413
414            Position { x, y }
415        } else {
416            return contact.position;
417        }
418    }
419
420    /// Watches for viewport updates from the scene manager.
421    pub async fn watch_viewport(self: Rc<Self>) {
422        let configuration_proxy = self.configuration_proxy.clone();
423        let mut viewport_stream = HangingGetStream::new(
424            configuration_proxy,
425            pointerinjector_config::SetupProxy::watch_viewport,
426        );
427        loop {
428            match viewport_stream.next().await {
429                Some(Ok(new_viewport)) => {
430                    // Update the viewport tracked by this handler.
431                    self.mutable_state.borrow_mut().viewport = Some(new_viewport.clone());
432
433                    // Update Scenic with the latest viewport.
434                    let injectors: Vec<pointerinjector::DeviceProxy> =
435                        self.mutable_state.borrow_mut().injectors.values().cloned().collect();
436                    for injector in injectors {
437                        let events = &[pointerinjector::Event {
438                            timestamp: Some(fuchsia_async::MonotonicInstant::now().into_nanos()),
439                            data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
440                            trace_flow_id: Some(fuchsia_trace::Id::new().into()),
441                            ..Default::default()
442                        }];
443                        injector.inject(events).await.expect("Failed to inject updated viewport.");
444                    }
445                }
446                Some(Err(e)) => {
447                    self.metrics_logger.log_error(
448                        InputPipelineErrorMetricDimensionEvent::TouchInjectorErrorWhileReadingViewportUpdate,
449                        std::format!("Error while reading viewport update: {}", e));
450                    return;
451                }
452                None => {
453                    self.metrics_logger.log_error(
454                        InputPipelineErrorMetricDimensionEvent::TouchInjectorViewportUpdateStreamTerminatedUnexpectedly,
455                        "Viewport update stream terminated unexpectedly");
456                    return;
457                }
458            }
459        }
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use super::*;
466    use crate::input_handler::InputHandler;
467    use crate::testing_utilities::{
468        create_fake_input_event, create_touch_contact, create_touch_pointer_sample_event,
469        create_touch_screen_event, create_touch_screen_event_with_handled, create_touchpad_event,
470        get_touch_screen_device_descriptor,
471    };
472    use assert_matches::assert_matches;
473    use maplit::hashmap;
474    use pretty_assertions::assert_eq;
475    use std::collections::HashSet;
476    use std::convert::TryFrom as _;
477    use std::ops::Add;
478    use {
479        fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
480        fuchsia_async as fasync,
481    };
482
483    const TOUCH_ID: u32 = 1;
484    const DISPLAY_WIDTH: f32 = 100.0;
485    const DISPLAY_HEIGHT: f32 = 100.0;
486
487    /// Returns an |input_device::InputDeviceDescriptor::Touchpad|.
488    fn get_touchpad_device_descriptor() -> input_device::InputDeviceDescriptor {
489        input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
490            device_id: 1,
491            contacts: vec![touch_binding::ContactDeviceDescriptor {
492                x_range: fidl_input_report::Range { min: 0, max: 100 },
493                y_range: fidl_input_report::Range { min: 0, max: 100 },
494                x_unit: fidl_input_report::Unit {
495                    type_: fidl_input_report::UnitType::Meters,
496                    exponent: -6,
497                },
498                y_unit: fidl_input_report::Unit {
499                    type_: fidl_input_report::UnitType::Meters,
500                    exponent: -6,
501                },
502                pressure_range: None,
503                width_range: None,
504                height_range: None,
505            }],
506        })
507    }
508
509    /// Handles |fidl_fuchsia_pointerinjector_configuration::SetupRequest::GetViewRefs|.
510    async fn handle_configuration_request_stream(
511        stream: &mut pointerinjector_config::SetupRequestStream,
512    ) {
513        if let Some(Ok(request)) = stream.next().await {
514            match request {
515                pointerinjector_config::SetupRequest::GetViewRefs { responder, .. } => {
516                    let context = fuchsia_scenic::ViewRefPair::new()
517                        .expect("Failed to create viewrefpair.")
518                        .view_ref;
519                    let target = fuchsia_scenic::ViewRefPair::new()
520                        .expect("Failed to create viewrefpair.")
521                        .view_ref;
522                    let _ = responder.send(context, target);
523                }
524                _ => {}
525            };
526        }
527    }
528
529    /// Handles |fidl_fuchsia_pointerinjector::DeviceRequest|s by asserting the `injector_stream`
530    /// gets `expected_event`.
531    async fn handle_device_request_stream(
532        mut injector_stream: pointerinjector::DeviceRequestStream,
533        expected_event: pointerinjector::Event,
534    ) {
535        match injector_stream.next().await {
536            Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
537                assert_eq!(events.len(), 1);
538                assert_eq!(events[0].timestamp, expected_event.timestamp);
539                assert_eq!(events[0].data, expected_event.data);
540                responder.send().expect("failed to respond");
541            }
542            Some(Err(e)) => panic!("FIDL error {}", e),
543            None => panic!("Expected another event."),
544        }
545    }
546
547    // Creates a |pointerinjector::Viewport|.
548    fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
549        pointerinjector::Viewport {
550            extents: Some([[min, min], [max, max]]),
551            viewport_to_context_transform: None,
552            ..Default::default()
553        }
554    }
555
556    // Tests that TouchInjectorHandler::watch_viewport() tracks viewport updates and notifies
557    // injectors about said updates.
558    #[fuchsia::test]
559    fn receives_viewport_updates() {
560        let mut exec = fasync::TestExecutor::new();
561
562        // Create touch handler.
563        let (configuration_proxy, mut configuration_request_stream) =
564            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
565        let (injector_registry_proxy, _injector_registry_request_stream) =
566            fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
567        let inspector = fuchsia_inspect::Inspector::default();
568        let test_node = inspector.root().create_child("test_node");
569        let touch_handler_fut = TouchInjectorHandler::new_handler(
570            configuration_proxy,
571            injector_registry_proxy,
572            Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
573            &test_node,
574            metrics::MetricsLogger::default(),
575        );
576        let config_request_stream_fut =
577            handle_configuration_request_stream(&mut configuration_request_stream);
578        let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
579            touch_handler_fut,
580            config_request_stream_fut,
581        ));
582        let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
583
584        // Add an injector.
585        let (injector_device_proxy, mut injector_device_request_stream) =
586            fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
587        touch_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
588
589        // This nested block is used to bound the lifetime of `watch_viewport_fut`.
590        {
591            // Request a viewport update.
592            let watch_viewport_fut = touch_handler.clone().watch_viewport();
593            futures::pin_mut!(watch_viewport_fut);
594            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
595
596            // Send a viewport update.
597            match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
598                Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
599                    responder, ..
600                })) => {
601                    responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
602                }
603                other => panic!("Received unexpected value: {:?}", other),
604            };
605            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
606
607            // Check that the injector received an updated viewport
608            exec.run_singlethreaded(async {
609                match injector_device_request_stream.next().await {
610                    Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
611                        assert_eq!(events.len(), 1);
612                        assert!(events[0].data.is_some());
613                        assert_eq!(
614                            events[0].data,
615                            Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
616                        );
617                        responder.send().expect("injector stream failed to respond.");
618                    }
619                    other => panic!("Received unexpected value: {:?}", other),
620                }
621            });
622
623            // Request viewport update.
624            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
625
626            // Send viewport update.
627            match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
628                Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
629                    responder, ..
630                })) => {
631                    responder
632                        .send(&create_viewport(100.0, 200.0))
633                        .expect("Failed to send viewport.");
634                }
635                other => panic!("Received unexpected value: {:?}", other),
636            };
637
638            // Process viewport update.
639            assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
640
641            // Check that the injector received an updated viewport
642            exec.run_singlethreaded(async {
643                match injector_device_request_stream.next().await {
644                    Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
645                        assert_eq!(events.len(), 1);
646                        assert!(events[0].data.is_some());
647                        assert_eq!(
648                            events[0].data,
649                            Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
650                        );
651                        responder.send().expect("injector stream failed to respond.");
652                    }
653                    other => panic!("Received unexpected value: {:?}", other),
654                }
655            });
656        }
657
658        // Check the viewport on the handler is accurate.
659        let expected_viewport = create_viewport(100.0, 200.0);
660        assert_eq!(touch_handler.mutable_state.borrow().viewport, Some(expected_viewport));
661    }
662
663    // Tests that an add contact event is dropped without a viewport.
664    #[fuchsia::test]
665    fn add_contact_drops_without_viewport() {
666        let mut exec = fasync::TestExecutor::new();
667
668        // Set up fidl streams.
669        let (configuration_proxy, mut configuration_request_stream) =
670            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
671        let (injector_registry_proxy, mut injector_registry_request_stream) =
672            fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
673        let config_request_stream_fut =
674            handle_configuration_request_stream(&mut configuration_request_stream);
675
676        let inspector = fuchsia_inspect::Inspector::default();
677        let test_node = inspector.root().create_child("test_node");
678
679        // Create TouchInjectorHandler.
680        let touch_handler_fut = TouchInjectorHandler::new_handler(
681            configuration_proxy,
682            injector_registry_proxy,
683            Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
684            &test_node,
685            metrics::MetricsLogger::default(),
686        );
687        let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
688            touch_handler_fut,
689            config_request_stream_fut,
690        ));
691        let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
692
693        // Create touch event.
694        let event_time = zx::MonotonicInstant::get();
695        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
696        let descriptor = get_touch_screen_device_descriptor();
697        let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
698            hashmap! {
699                fidl_ui_input::PointerEventPhase::Add
700                    => vec![contact.clone()],
701            },
702            event_time,
703            &descriptor,
704        ))
705        .unwrap();
706
707        // Try to handle the event.
708        // Subtle: We handle the event on a clone of the handler because the call consumes the
709        // handler, whose reference to `injectory_registry_proxy` is needed to keep
710        // `injector_registry_request_stream` alive.
711        let mut handle_event_fut = touch_handler.clone().handle_unhandled_input_event(input_event);
712        let _ = exec.run_until_stalled(&mut handle_event_fut);
713
714        // Injector should not receive anything because the handler has no viewport.
715        let mut ir_fut = injector_registry_request_stream.next();
716        assert_matches!(exec.run_until_stalled(&mut ir_fut), futures::task::Poll::Pending);
717    }
718
719    // Tests that an add contact event is handled correctly with a viewport.
720    #[fuchsia::test]
721    fn add_contact_succeeds_with_viewport() {
722        let mut exec = fasync::TestExecutor::new();
723
724        // Create touch handler.
725        let (configuration_proxy, mut configuration_request_stream) =
726            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
727        let (injector_registry_proxy, _injector_registry_request_stream) =
728            fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
729        let inspector = fuchsia_inspect::Inspector::default();
730        let test_node = inspector.root().create_child("test_node");
731        let touch_handler_fut = TouchInjectorHandler::new_handler(
732            configuration_proxy,
733            injector_registry_proxy,
734            Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
735            &test_node,
736            metrics::MetricsLogger::default(),
737        );
738        let config_request_stream_fut =
739            handle_configuration_request_stream(&mut configuration_request_stream);
740        let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
741            touch_handler_fut,
742            config_request_stream_fut,
743        ));
744        let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
745
746        // Add an injector.
747        let (injector_device_proxy, mut injector_device_request_stream) =
748            fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
749        touch_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
750
751        // Request a viewport update.
752        let watch_viewport_fut = fasync::Task::local(touch_handler.clone().watch_viewport());
753        futures::pin_mut!(watch_viewport_fut);
754        assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
755
756        // Send a viewport update.
757        match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
758            Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
759                responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
760            }
761            other => panic!("Received unexpected value: {:?}", other),
762        };
763        assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
764
765        // Check that the injector received an updated viewport
766        exec.run_singlethreaded(async {
767            match injector_device_request_stream.next().await {
768                Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
769                    assert_eq!(events.len(), 1);
770                    assert!(events[0].data.is_some());
771                    assert_eq!(
772                        events[0].data,
773                        Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
774                    );
775                    responder.send().expect("injector stream failed to respond.");
776                }
777                other => panic!("Received unexpected value: {:?}", other),
778            }
779        });
780
781        // Create touch event.
782        let event_time = zx::MonotonicInstant::get();
783        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
784        let descriptor = get_touch_screen_device_descriptor();
785        let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
786            hashmap! {
787                fidl_ui_input::PointerEventPhase::Add
788                    => vec![contact.clone()],
789            },
790            event_time,
791            &descriptor,
792        ))
793        .unwrap();
794
795        // Handle event.
796        let handle_event_fut = touch_handler.clone().handle_unhandled_input_event(input_event);
797
798        // Declare expected event.
799        let expected_event = create_touch_pointer_sample_event(
800            pointerinjector::EventPhase::Add,
801            &contact,
802            Position { x: 20.0, y: 40.0 },
803            event_time,
804        );
805
806        // Await all futures concurrently. If this completes, then the touch event was handled and
807        // matches `expected_event`.
808        let device_fut =
809            handle_device_request_stream(injector_device_request_stream, expected_event);
810        let (handle_result, _) =
811            exec.run_singlethreaded(futures::future::join(handle_event_fut, device_fut));
812
813        // No unhandled events.
814        assert_matches!(
815            handle_result.as_slice(),
816            [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
817        );
818    }
819
820    // Tests that an add touchpad contact event with viewport is unhandled and not send to scenic.
821    #[fuchsia::test]
822    fn add_touchpad_contact_with_viewport() {
823        let mut exec = fasync::TestExecutor::new();
824
825        // Create touch handler.
826        let (configuration_proxy, mut configuration_request_stream) =
827            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
828        let (injector_registry_proxy, mut injector_registry_request_stream) =
829            fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
830        let inspector = fuchsia_inspect::Inspector::default();
831        let test_node = inspector.root().create_child("test_node");
832        let touch_handler_fut = TouchInjectorHandler::new_handler(
833            configuration_proxy,
834            injector_registry_proxy,
835            Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
836            &test_node,
837            metrics::MetricsLogger::default(),
838        );
839        let config_request_stream_fut =
840            handle_configuration_request_stream(&mut configuration_request_stream);
841        let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
842            touch_handler_fut,
843            config_request_stream_fut,
844        ));
845        let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
846
847        // Add an injector.
848        let (injector_device_proxy, mut injector_device_request_stream) =
849            fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
850        touch_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
851
852        // Request a viewport update.
853        let watch_viewport_fut = fasync::Task::local(touch_handler.clone().watch_viewport());
854        futures::pin_mut!(watch_viewport_fut);
855        assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
856
857        // Send a viewport update.
858        match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
859            Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
860                responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
861            }
862            other => panic!("Received unexpected value: {:?}", other),
863        };
864        assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
865
866        // Check that the injector received an updated viewport
867        exec.run_singlethreaded(async {
868            match injector_device_request_stream.next().await {
869                Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
870                    assert_eq!(events.len(), 1);
871                    assert!(events[0].data.is_some());
872                    assert_eq!(
873                        events[0].data,
874                        Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
875                    );
876                    responder.send().expect("injector stream failed to respond.");
877                }
878                other => panic!("Received unexpected value: {:?}", other),
879            }
880        });
881
882        // Create touch event.
883        let event_time = zx::MonotonicInstant::get();
884        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
885        let descriptor = get_touchpad_device_descriptor();
886        let input_event = input_device::UnhandledInputEvent::try_from(create_touchpad_event(
887            vec![contact.clone()],
888            HashSet::new(),
889            event_time,
890            &descriptor,
891        ))
892        .unwrap();
893
894        // Handle event.
895        let handle_event_fut = touch_handler.clone().handle_unhandled_input_event(input_event);
896
897        let handle_result = exec.run_singlethreaded(handle_event_fut);
898
899        // Event is not handled.
900        assert_matches!(
901            handle_result.as_slice(),
902            [input_device::InputEvent { handled: input_device::Handled::No, .. }]
903        );
904
905        // Injector should not receive anything because the handler does not support touchpad yet.
906        let mut ir_fut = injector_registry_request_stream.next();
907        assert_matches!(exec.run_until_stalled(&mut ir_fut), futures::task::Poll::Pending);
908    }
909
910    #[fuchsia::test(allow_stalls = false)]
911    async fn touch_injector_handler_initialized_with_inspect_node() {
912        let (configuration_proxy, mut configuration_request_stream) =
913            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
914        let inspector = fuchsia_inspect::Inspector::default();
915        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
916        let touch_handler_fut = TouchInjectorHandler::new_with_config_proxy(
917            configuration_proxy,
918            Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
919            &fake_handlers_node,
920            metrics::MetricsLogger::default(),
921        );
922        let config_request_stream_fut =
923            handle_configuration_request_stream(&mut configuration_request_stream);
924        let (touch_handler_res, _) = futures::join!(touch_handler_fut, config_request_stream_fut,);
925        let _handler = touch_handler_res.expect("Failed to create touch handler.");
926        diagnostics_assertions::assert_data_tree!(inspector, root: {
927            input_handlers_node: {
928                touch_injector_handler: {
929                    events_received_count: 0u64,
930                    events_handled_count: 0u64,
931                    last_received_timestamp_ns: 0u64,
932                    "fuchsia.inspect.Health": {
933                        status: "STARTING_UP",
934                        // Timestamp value is unpredictable and not relevant in this context,
935                        // so we only assert that the property is present.
936                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
937                    },
938                }
939            }
940        });
941    }
942
943    #[fuchsia::test(allow_stalls = false)]
944    async fn touch_injector_handler_inspect_counts_events() {
945        // Set up fidl streams.
946        let (configuration_proxy, mut configuration_request_stream) =
947            fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
948        let (injector_registry_proxy, _) =
949            fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
950
951        let inspector = fuchsia_inspect::Inspector::default();
952        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
953
954        // Create touch handler.
955        let touch_handler_fut = TouchInjectorHandler::new_handler(
956            configuration_proxy,
957            injector_registry_proxy,
958            Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
959            &fake_handlers_node,
960            metrics::MetricsLogger::default(),
961        );
962        let config_request_stream_fut =
963            handle_configuration_request_stream(&mut configuration_request_stream);
964        let (touch_handler_res, _) = futures::join!(touch_handler_fut, config_request_stream_fut);
965        let touch_handler = touch_handler_res.expect("Failed to create touch handler");
966
967        let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
968        let descriptor = get_touch_screen_device_descriptor();
969        let event_time1 = zx::MonotonicInstant::get();
970        let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
971        let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
972
973        let input_events = vec![
974            create_touch_screen_event(
975                hashmap! {
976                    fidl_ui_input::PointerEventPhase::Add
977                        => vec![contact.clone()],
978                },
979                event_time1,
980                &descriptor,
981            ),
982            create_touch_screen_event(
983                hashmap! {
984                    fidl_ui_input::PointerEventPhase::Move
985                        => vec![contact.clone()],
986                },
987                event_time2,
988                &descriptor,
989            ),
990            // Should not count non-touch input event.
991            create_fake_input_event(event_time2),
992            // Should not count received event that has already been handled.
993            create_touch_screen_event_with_handled(
994                hashmap! {
995                    fidl_ui_input::PointerEventPhase::Move
996                        => vec![contact.clone()],
997                },
998                event_time2,
999                &descriptor,
1000                input_device::Handled::Yes,
1001            ),
1002            create_touch_screen_event(
1003                hashmap! {
1004                    fidl_ui_input::PointerEventPhase::Remove
1005                        => vec![contact.clone()],
1006                },
1007                event_time3,
1008                &descriptor,
1009            ),
1010        ];
1011
1012        for input_event in input_events {
1013            touch_handler.clone().handle_input_event(input_event).await;
1014        }
1015
1016        let last_received_event_time: u64 = event_time3.into_nanos().try_into().unwrap();
1017
1018        diagnostics_assertions::assert_data_tree!(inspector, root: {
1019            input_handlers_node: {
1020                touch_injector_handler: {
1021                    events_received_count: 3u64,
1022                    events_handled_count: 3u64,
1023                    last_received_timestamp_ns: last_received_event_time,
1024                    "fuchsia.inspect.Health": {
1025                        status: "STARTING_UP",
1026                        // Timestamp value is unpredictable and not relevant in this context,
1027                        // so we only assert that the property is present.
1028                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
1029                    },
1030                }
1031            }
1032        });
1033    }
1034}