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