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