1#![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, Proxy};
13use fidl::AsHandleRef;
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
80#[async_trait(?Send)]
81impl UnhandledInputHandler for TouchInjectorHandler {
82 async fn handle_unhandled_input_event(
83 self: Rc<Self>,
84 unhandled_input_event: input_device::UnhandledInputEvent,
85 ) -> Vec<input_device::InputEvent> {
86 fuchsia_trace::duration!(c"input", c"presentation_on_event");
87 match unhandled_input_event {
88 input_device::UnhandledInputEvent {
89 device_event: input_device::InputDeviceEvent::TouchScreen(ref touch_event),
90 device_descriptor:
91 input_device::InputDeviceDescriptor::TouchScreen(ref touch_device_descriptor),
92 event_time,
93 trace_id,
94 } => {
95 self.inspect_status.count_received_event(input_device::InputEvent::from(
96 unhandled_input_event.clone(),
97 ));
98 fuchsia_trace::duration!(c"input", c"touch_injector_handler");
99 if let Some(trace_id) = trace_id {
100 fuchsia_trace::flow_end!(c"input", c"event_in_input_pipeline", trace_id.into());
101 }
102 if touch_event.injector_contacts.values().all(|vec| vec.is_empty()) {
103 let touch_buttons_event = Self::create_touch_buttons_event(
104 &touch_event,
105 event_time,
106 &touch_device_descriptor,
107 );
108
109 self.send_event_to_listeners(&touch_buttons_event).await;
111
112 self.mutable_state.borrow_mut().last_button_event = Some(touch_buttons_event);
114 } else if touch_event.pressed_buttons.is_empty() {
115 if let Err(e) = self.ensure_injector_registered(&touch_device_descriptor).await
117 {
118 self.metrics_logger.log_error(
119 InputPipelineErrorMetricDimensionEvent::TouchInjectorEnsureInjectorRegisteredFailed,
120 std::format!("ensure_injector_registered failed: {}", e));
121 }
122
123 if let Err(e) = self
125 .send_event_to_scenic(&touch_event, &touch_device_descriptor, event_time)
126 .await
127 {
128 self.metrics_logger.log_error(
129 InputPipelineErrorMetricDimensionEvent::TouchInjectorSendEventToScenicFailed,
130 std::format!("send_event_to_scenic failed: {}", e));
131 }
132 }
133
134 self.inspect_status.count_handled_event();
136 vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
137 }
138 _ => vec![input_device::InputEvent::from(unhandled_input_event)],
139 }
140 }
141
142 fn set_handler_healthy(self: std::rc::Rc<Self>) {
143 self.inspect_status.health_node.borrow_mut().set_ok();
144 }
145
146 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
147 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
148 }
149}
150
151impl TouchInjectorHandler {
152 pub async fn new(
164 display_size: Size,
165 input_handlers_node: &fuchsia_inspect::Node,
166 metrics_logger: metrics::MetricsLogger,
167 ) -> Result<Rc<Self>, Error> {
168 let configuration_proxy = connect_to_protocol::<pointerinjector_config::SetupMarker>()?;
169 let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
170
171 Self::new_handler(
172 configuration_proxy,
173 injector_registry_proxy,
174 display_size,
175 input_handlers_node,
176 metrics_logger,
177 )
178 .await
179 }
180
181 pub async fn new_with_config_proxy(
196 configuration_proxy: pointerinjector_config::SetupProxy,
197 display_size: Size,
198 input_handlers_node: &fuchsia_inspect::Node,
199 metrics_logger: metrics::MetricsLogger,
200 ) -> Result<Rc<Self>, Error> {
201 let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
202 Self::new_handler(
203 configuration_proxy,
204 injector_registry_proxy,
205 display_size,
206 input_handlers_node,
207 metrics_logger,
208 )
209 .await
210 }
211
212 async fn new_handler(
228 configuration_proxy: pointerinjector_config::SetupProxy,
229 injector_registry_proxy: pointerinjector::RegistryProxy,
230 display_size: Size,
231 input_handlers_node: &fuchsia_inspect::Node,
232 metrics_logger: metrics::MetricsLogger,
233 ) -> Result<Rc<Self>, Error> {
234 let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
236
237 let inspect_status = InputHandlerStatus::new(
238 input_handlers_node,
239 "touch_injector_handler",
240 false,
241 );
242 let handler = Rc::new(Self {
243 mutable_state: RefCell::new(MutableState {
244 viewport: None,
245 injectors: HashMap::new(),
246 listeners: HashMap::new(),
247 last_button_event: None,
248 send_event_task_tracker: LocalTaskTracker::new(),
249 }),
250 context_view_ref,
251 target_view_ref,
252 display_size,
253 injector_registry_proxy,
254 configuration_proxy,
255 inspect_status,
256 metrics_logger,
257 });
258
259 Ok(handler)
260 }
261
262 async fn ensure_injector_registered(
268 self: &Rc<Self>,
269 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
270 ) -> Result<(), anyhow::Error> {
271 if self.mutable_state.borrow().injectors.contains_key(&touch_descriptor.device_id) {
272 return Ok(());
273 }
274
275 let (device_proxy, device_server) = create_proxy::<pointerinjector::DeviceMarker>();
277 let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
278 .context("Failed to duplicate context view ref.")?;
279 let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
280 .context("Failed to duplicate target view ref.")?;
281 let viewport = self.mutable_state.borrow().viewport.clone();
282 if viewport.is_none() {
283 return Err(anyhow::format_err!(
286 "Received a touch event without a viewport to inject into."
287 ));
288 }
289 let config = pointerinjector::Config {
290 device_id: Some(touch_descriptor.device_id),
291 device_type: Some(pointerinjector::DeviceType::Touch),
292 context: Some(pointerinjector::Context::View(context)),
293 target: Some(pointerinjector::Target::View(target)),
294 viewport,
295 dispatch_policy: Some(pointerinjector::DispatchPolicy::TopHitAndAncestorsInTarget),
296 scroll_v_range: None,
297 scroll_h_range: None,
298 buttons: None,
299 ..Default::default()
300 };
301
302 self.mutable_state.borrow_mut().injectors.insert(touch_descriptor.device_id, device_proxy);
304
305 self.injector_registry_proxy
307 .register(config, device_server)
308 .await
309 .context("Failed to register injector.")?;
310 log::info!("Registered injector with device id {:?}", touch_descriptor.device_id);
311
312 Ok(())
313 }
314
315 async fn send_event_to_scenic(
322 &self,
323 touch_event: &touch_binding::TouchScreenEvent,
324 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
325 event_time: zx::MonotonicInstant,
326 ) -> Result<(), anyhow::Error> {
327 let ordered_phases = vec![
329 pointerinjector::EventPhase::Add,
330 pointerinjector::EventPhase::Change,
331 pointerinjector::EventPhase::Remove,
332 ];
333
334 fuchsia_trace::duration_begin!(c"input", c"touch-inject-into-scenic");
339
340 let mut events: Vec<pointerinjector::Event> = vec![];
341 for phase in ordered_phases {
342 let contacts: Vec<touch_binding::TouchContact> = touch_event
343 .injector_contacts
344 .get(&phase)
345 .map_or(vec![], |contacts| contacts.to_owned());
346 let new_events = contacts.into_iter().map(|contact| {
347 Self::create_pointer_sample_event(
348 phase,
349 &contact,
350 touch_descriptor,
351 &self.display_size,
352 event_time,
353 )
354 });
355 events.extend(new_events);
356 }
357
358 let injector =
359 self.mutable_state.borrow().injectors.get(&touch_descriptor.device_id).cloned();
360 if let Some(injector) = injector {
361 let fut = injector.inject(&events);
362 fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
364 let _ = fut.await;
365 Ok(())
366 } else {
367 fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
368 Err(anyhow::format_err!(
369 "No injector found for touch device {}.",
370 touch_descriptor.device_id
371 ))
372 }
373 }
374
375 fn create_pointer_sample_event(
384 phase: pointerinjector::EventPhase,
385 contact: &touch_binding::TouchContact,
386 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
387 display_size: &Size,
388 event_time: zx::MonotonicInstant,
389 ) -> pointerinjector::Event {
390 let position =
391 Self::display_coordinate_from_contact(&contact, &touch_descriptor, display_size);
392 let pointer_sample = pointerinjector::PointerSample {
393 pointer_id: Some(contact.id),
394 phase: Some(phase),
395 position_in_viewport: Some([position.x, position.y]),
396 scroll_v: None,
397 scroll_h: None,
398 pressed_buttons: None,
399 ..Default::default()
400 };
401 let data = pointerinjector::Data::PointerSample(pointer_sample);
402
403 let trace_flow_id = fuchsia_trace::Id::random();
404 let event = pointerinjector::Event {
405 timestamp: Some(event_time.into_nanos()),
406 data: Some(data),
407 trace_flow_id: Some(trace_flow_id.into()),
408 ..Default::default()
409 };
410
411 fuchsia_trace::flow_begin!(c"input", c"dispatch_event_to_scenic", trace_flow_id);
412
413 event
414 }
415
416 fn display_coordinate_from_contact(
430 contact: &touch_binding::TouchContact,
431 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
432 display_size: &Size,
433 ) -> Position {
434 if let Some(contact_descriptor) = touch_descriptor.contacts.first() {
435 let x_range: f32 =
437 contact_descriptor.x_range.max as f32 - contact_descriptor.x_range.min as f32;
438 let x_wrt_range: f32 = contact.position.x - contact_descriptor.x_range.min as f32;
439 let x: f32 = (display_size.width * x_wrt_range) / x_range;
440
441 let y_range: f32 =
443 contact_descriptor.y_range.max as f32 - contact_descriptor.y_range.min as f32;
444 let y_wrt_range: f32 = contact.position.y - contact_descriptor.y_range.min as f32;
445 let y: f32 = (display_size.height * y_wrt_range) / y_range;
446
447 Position { x, y }
448 } else {
449 return contact.position;
450 }
451 }
452
453 pub async fn watch_viewport(self: Rc<Self>) {
455 let configuration_proxy = self.configuration_proxy.clone();
456 let mut viewport_stream = HangingGetStream::new(
457 configuration_proxy,
458 pointerinjector_config::SetupProxy::watch_viewport,
459 );
460 loop {
461 match viewport_stream.next().await {
462 Some(Ok(new_viewport)) => {
463 self.mutable_state.borrow_mut().viewport = Some(new_viewport.clone());
465
466 let injectors: Vec<pointerinjector::DeviceProxy> =
468 self.mutable_state.borrow_mut().injectors.values().cloned().collect();
469 for injector in injectors {
470 let events = &[pointerinjector::Event {
471 timestamp: Some(fuchsia_async::MonotonicInstant::now().into_nanos()),
472 data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
473 trace_flow_id: Some(fuchsia_trace::Id::random().into()),
474 ..Default::default()
475 }];
476 injector.inject(events).await.expect("Failed to inject updated viewport.");
477 }
478 }
479 Some(Err(e)) => {
480 self.metrics_logger.log_error(
481 InputPipelineErrorMetricDimensionEvent::TouchInjectorErrorWhileReadingViewportUpdate,
482 std::format!("Error while reading viewport update: {}", e));
483 return;
484 }
485 None => {
486 self.metrics_logger.log_error(
487 InputPipelineErrorMetricDimensionEvent::TouchInjectorViewportUpdateStreamTerminatedUnexpectedly,
488 "Viewport update stream terminated unexpectedly");
489 return;
490 }
491 }
492 }
493 }
494
495 fn create_touch_buttons_event(
502 event: &touch_binding::TouchScreenEvent,
503 event_time: zx::MonotonicInstant,
504 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
505 ) -> fidl_ui_input::TouchButtonsEvent {
506 let pressed_buttons = match event.pressed_buttons.len() {
507 0 => None,
508 _ => Some(
509 event
510 .pressed_buttons
511 .clone()
512 .into_iter()
513 .map(|button| match button {
514 fidl_input_report::TouchButton::Palm => fidl_ui_input::TouchButton::Palm,
515 fidl_input_report::TouchButton::__SourceBreaking { unknown_ordinal: n } => {
516 fidl_ui_input::TouchButton::__SourceBreaking {
517 unknown_ordinal: n as u32,
518 }
519 }
520 })
521 .collect::<Vec<_>>(),
522 ),
523 };
524 fidl_ui_input::TouchButtonsEvent {
525 event_time: Some(event_time),
526 device_info: Some(fidl_ui_input::TouchDeviceInfo {
527 id: Some(touch_descriptor.device_id),
528 ..Default::default()
529 }),
530 pressed_buttons,
531 ..Default::default()
532 }
533 }
534
535 async fn send_event_to_listeners(self: &Rc<Self>, event: &fidl_ui_input::TouchButtonsEvent) {
540 let tracker = &self.mutable_state.borrow().send_event_task_tracker;
541
542 for (handle, listener) in &self.mutable_state.borrow().listeners {
543 let weak_handler = Rc::downgrade(&self);
544 let listener_clone = listener.clone();
545 let handle_clone = handle.clone();
546 let event_to_send = event.clone();
547 let fut = async move {
548 match listener_clone.on_event(&event_to_send).await {
549 Ok(_) => {}
550 Err(e) => {
551 if let Some(handler) = weak_handler.upgrade() {
552 handler.mutable_state.borrow_mut().listeners.remove(&handle_clone);
553 log::info!(
554 "Unregistering listener; unable to send TouchButtonsEvent: {:?}",
555 e
556 )
557 }
558 }
559 }
560 };
561
562 let metrics_logger_clone = self.metrics_logger.clone();
563 tracker.track(metrics_logger_clone, fasync::Task::local(fut));
564 }
565 }
566
567 pub async fn register_listener_proxy(
572 self: &Rc<Self>,
573 proxy: fidl_ui_policy::TouchButtonsListenerProxy,
574 ) {
575 self.mutable_state
576 .borrow_mut()
577 .listeners
578 .insert(proxy.as_channel().raw_handle(), proxy.clone());
579
580 if let Some(event) = &self.mutable_state.borrow().last_button_event {
582 let event_to_send = event.clone();
583 let fut = async move {
584 match proxy.on_event(&event_to_send).await {
585 Ok(_) => {}
586 Err(e) => {
587 log::info!("Failed to send touch buttons event to listener {:?}", e)
588 }
589 }
590 };
591 let metrics_logger_clone = self.metrics_logger.clone();
592 self.mutable_state
593 .borrow()
594 .send_event_task_tracker
595 .track(metrics_logger_clone, fasync::Task::local(fut));
596 }
597 }
598}
599
600#[derive(Debug)]
603pub struct LocalTaskTracker {
604 sender: mpsc::UnboundedSender<fasync::Task<()>>,
605 _receiver_task: fasync::Task<()>,
606}
607
608impl LocalTaskTracker {
609 pub fn new() -> Self {
610 let (sender, receiver) = mpsc::unbounded();
611 let receiver_task = fasync::Task::local(async move {
612 receiver.for_each_concurrent(None, |task: fasync::Task<()>| task).await
614 });
615
616 Self { sender, _receiver_task: receiver_task }
617 }
618
619 pub fn track(&self, metrics_logger: metrics::MetricsLogger, task: fasync::Task<()>) {
621 match self.sender.unbounded_send(task) {
622 Ok(_) => {}
623 Err(e) => {
627 metrics_logger.log_error(
628 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
629 std::format!("Unexpected {e:?} while pushing task"),
630 );
631 }
632 };
633 }
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639 use crate::input_handler::InputHandler;
640 use crate::testing_utilities::{
641 create_fake_input_event, create_touch_contact, create_touch_pointer_sample_event,
642 create_touch_screen_event, create_touch_screen_event_with_handled, create_touchpad_event,
643 get_touch_screen_device_descriptor,
644 };
645 use assert_matches::assert_matches;
646 use futures::{FutureExt, TryStreamExt};
647 use maplit::hashmap;
648 use pretty_assertions::assert_eq;
649 use std::collections::HashSet;
650 use std::convert::TryFrom as _;
651 use std::ops::Add;
652 use {
653 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
654 fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
655 };
656
657 const TOUCH_ID: u32 = 1;
658 const DISPLAY_WIDTH: f32 = 100.0;
659 const DISPLAY_HEIGHT: f32 = 100.0;
660
661 struct TestFixtures {
662 touch_handler: Rc<TouchInjectorHandler>,
663 device_listener_proxy: fidl_ui_policy::DeviceListenerRegistryProxy,
664 injector_registry_request_stream: pointerinjector::RegistryRequestStream,
665 configuration_request_stream: pointerinjector_config::SetupRequestStream,
666 inspector: fuchsia_inspect::Inspector,
667 _test_node: fuchsia_inspect::Node,
668 }
669
670 fn spawn_device_listener_registry_server(
671 handler: Rc<TouchInjectorHandler>,
672 ) -> fidl_ui_policy::DeviceListenerRegistryProxy {
673 let (device_listener_proxy, mut device_listener_stream) =
674 fidl::endpoints::create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>(
675 );
676
677 fasync::Task::local(async move {
678 loop {
679 match device_listener_stream.try_next().await {
680 Ok(Some(
681 fidl_ui_policy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
682 listener,
683 responder,
684 },
685 )) => {
686 handler.register_listener_proxy(listener.into_proxy()).await;
687 let _ = responder.send();
688 }
689 Ok(Some(_)) => {
690 panic!("Unexpected registration");
691 }
692 Ok(None) => {
693 break;
694 }
695 Err(e) => {
696 panic!("Error handling device listener registry request stream: {}", e);
697 }
698 }
699 }
700 })
701 .detach();
702
703 device_listener_proxy
704 }
705
706 impl TestFixtures {
707 async fn new() -> Self {
708 let inspector = fuchsia_inspect::Inspector::default();
709 let test_node = inspector.root().create_child("test_node");
710 let (configuration_proxy, mut configuration_request_stream) =
711 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
712 let (injector_registry_proxy, injector_registry_request_stream) =
713 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
714
715 let touch_handler_fut = TouchInjectorHandler::new_handler(
716 configuration_proxy,
717 injector_registry_proxy,
718 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
719 &test_node,
720 metrics::MetricsLogger::default(),
721 );
722
723 let handle_initial_request_fut = async {
724 match configuration_request_stream.next().await {
725 Some(Ok(pointerinjector_config::SetupRequest::GetViewRefs {
726 responder,
727 ..
728 })) => {
729 let context = fuchsia_scenic::ViewRefPair::new()
730 .expect("Failed to create viewrefpair.")
731 .view_ref;
732 let target = fuchsia_scenic::ViewRefPair::new()
733 .expect("Failed to create viewrefpair.")
734 .view_ref;
735 let _ = responder.send(context, target);
736 }
737 other => panic!("Expected GetViewRefs request, got {:?}", other),
738 }
739 };
740
741 let (touch_handler_res, _) =
742 futures::future::join(touch_handler_fut, handle_initial_request_fut).await;
743
744 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
745 let device_listener_proxy =
746 spawn_device_listener_registry_server(touch_handler.clone());
747
748 TestFixtures {
749 touch_handler,
750 device_listener_proxy,
751 injector_registry_request_stream,
752 configuration_request_stream,
753 inspector,
754 _test_node: test_node,
755 }
756 }
757 }
758
759 fn get_touchpad_device_descriptor() -> input_device::InputDeviceDescriptor {
761 input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
762 device_id: 1,
763 contacts: vec![touch_binding::ContactDeviceDescriptor {
764 x_range: fidl_input_report::Range { min: 0, max: 100 },
765 y_range: fidl_input_report::Range { min: 0, max: 100 },
766 x_unit: fidl_input_report::Unit {
767 type_: fidl_input_report::UnitType::Meters,
768 exponent: -6,
769 },
770 y_unit: fidl_input_report::Unit {
771 type_: fidl_input_report::UnitType::Meters,
772 exponent: -6,
773 },
774 pressure_range: None,
775 width_range: None,
776 height_range: None,
777 }],
778 })
779 }
780
781 async fn handle_device_request_stream(
784 mut injector_stream: pointerinjector::DeviceRequestStream,
785 expected_event: pointerinjector::Event,
786 ) {
787 match injector_stream.next().await {
788 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
789 assert_eq!(events.len(), 1);
790 assert_eq!(events[0].timestamp, expected_event.timestamp);
791 assert_eq!(events[0].data, expected_event.data);
792 responder.send().expect("failed to respond");
793 }
794 Some(Err(e)) => panic!("FIDL error {}", e),
795 None => panic!("Expected another event."),
796 }
797 }
798
799 fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
801 pointerinjector::Viewport {
802 extents: Some([[min, min], [max, max]]),
803 viewport_to_context_transform: None,
804 ..Default::default()
805 }
806 }
807
808 #[fuchsia::test]
809 async fn events_with_pressed_buttons_are_sent_to_listener() {
810 let fixtures = TestFixtures::new().await;
811 let (listener, mut listener_stream) =
812 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
813 fixtures
814 .device_listener_proxy
815 .register_touch_buttons_listener(listener)
816 .await
817 .expect("Failed to register listener.");
818
819 let descriptor = get_touch_screen_device_descriptor();
820 let event_time = zx::MonotonicInstant::get();
821 let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
822
823 let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
824
825 let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
826 event_time: Some(event_time),
827 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
828 ..Default::default()
829 };
830
831 assert_matches!(
832 listener_stream.next().await,
833 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
834 event,
835 responder,
836 })) => {
837 assert_eq!(event, expected_touch_buttons_event);
838 let _ = responder.send();
839 }
840 );
841 }
842
843 #[fuchsia::test]
844 async fn events_with_contacts_are_not_sent_to_listener() {
845 let fixtures = TestFixtures::new().await;
846 let (listener, mut listener_stream) =
847 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
848 fixtures
849 .device_listener_proxy
850 .register_touch_buttons_listener(listener)
851 .await
852 .expect("Failed to register listener.");
853
854 let descriptor = get_touch_screen_device_descriptor();
855 let event_time = zx::MonotonicInstant::get();
856 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
857 let input_event = create_touch_screen_event(
858 hashmap! {
859 fidl_ui_input::PointerEventPhase::Add
860 => vec![contact.clone()],
861 },
862 event_time,
863 &descriptor,
864 );
865
866 let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
867
868 assert!(listener_stream.next().now_or_never().is_none());
869 }
870
871 #[fuchsia::test]
872 async fn multiple_listeners_receive_pressed_button_events() {
873 let fixtures = TestFixtures::new().await;
874 let (first_listener, mut first_listener_stream) =
875 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
876 let (second_listener, mut second_listener_stream) =
877 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
878 fixtures
879 .device_listener_proxy
880 .register_touch_buttons_listener(first_listener)
881 .await
882 .expect("Failed to register listener.");
883 fixtures
884 .device_listener_proxy
885 .register_touch_buttons_listener(second_listener)
886 .await
887 .expect("Failed to register listener.");
888
889 let descriptor = get_touch_screen_device_descriptor();
890 let event_time = zx::MonotonicInstant::get();
891 let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
892
893 let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
894
895 let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
896 event_time: Some(event_time),
897 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
898 ..Default::default()
899 };
900
901 assert_matches!(
902 first_listener_stream.next().await,
903 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
904 event,
905 responder,
906 })) => {
907 assert_eq!(event, expected_touch_buttons_event);
908 let _ = responder.send();
909 }
910 );
911 assert_matches!(
912 second_listener_stream.next().await,
913 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
914 event,
915 responder,
916 })) => {
917 assert_eq!(event, expected_touch_buttons_event);
918 let _ = responder.send();
919 }
920 );
921 }
922
923 #[fuchsia::test]
926 async fn receives_viewport_updates() {
927 let mut fixtures = TestFixtures::new().await;
928
929 let (injector_device_proxy, mut injector_device_request_stream) =
931 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
932 fixtures
933 .touch_handler
934 .mutable_state
935 .borrow_mut()
936 .injectors
937 .insert(1, injector_device_proxy);
938
939 {
941 let _watch_viewport_task =
943 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
944
945 match fixtures.configuration_request_stream.next().await {
947 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
948 responder, ..
949 })) => {
950 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
951 }
952 other => panic!("Received unexpected value: {:?}", other),
953 };
954
955 match injector_device_request_stream.next().await {
957 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
958 assert_eq!(events.len(), 1);
959 assert!(events[0].data.is_some());
960 assert_eq!(
961 events[0].data,
962 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
963 );
964 responder.send().expect("injector stream failed to respond.");
965 }
966 other => panic!("Received unexpected value: {:?}", other),
967 }
968
969 match fixtures.configuration_request_stream.next().await {
972 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
973 responder, ..
974 })) => {
975 responder
976 .send(&create_viewport(100.0, 200.0))
977 .expect("Failed to send viewport.");
978 }
979 other => panic!("Received unexpected value: {:?}", other),
980 };
981
982 match injector_device_request_stream.next().await {
984 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
985 assert_eq!(events.len(), 1);
986 assert!(events[0].data.is_some());
987 assert_eq!(
988 events[0].data,
989 Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
990 );
991 responder.send().expect("injector stream failed to respond.");
992 }
993 other => panic!("Received unexpected value: {:?}", other),
994 }
995 }
996
997 let expected_viewport = create_viewport(100.0, 200.0);
999 assert_eq!(fixtures.touch_handler.mutable_state.borrow().viewport, Some(expected_viewport));
1000 }
1001
1002 #[fuchsia::test]
1004 async fn add_contact_drops_without_viewport() {
1005 let mut fixtures = TestFixtures::new().await;
1006
1007 let event_time = zx::MonotonicInstant::get();
1009 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1010 let descriptor = get_touch_screen_device_descriptor();
1011 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1012 hashmap! {
1013 fidl_ui_input::PointerEventPhase::Add
1014 => vec![contact.clone()],
1015 },
1016 event_time,
1017 &descriptor,
1018 ))
1019 .unwrap();
1020
1021 fixtures.touch_handler.mutable_state.borrow_mut().viewport = None;
1023
1024 let _ = fixtures.touch_handler.clone().handle_unhandled_input_event(input_event).await;
1026
1027 assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1029 }
1030
1031 #[fuchsia::test]
1033 async fn add_contact_succeeds_with_viewport() {
1034 let mut fixtures = TestFixtures::new().await;
1035
1036 let (injector_device_proxy, mut injector_device_request_stream) =
1038 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1039 fixtures
1040 .touch_handler
1041 .mutable_state
1042 .borrow_mut()
1043 .injectors
1044 .insert(1, injector_device_proxy);
1045
1046 let _watch_viewport_task =
1048 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1049
1050 match fixtures.configuration_request_stream.next().await {
1052 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1053 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1054 }
1055 other => panic!("Received unexpected value: {:?}", other),
1056 };
1057
1058 match injector_device_request_stream.next().await {
1060 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
1061 assert_eq!(events.len(), 1);
1062 assert!(events[0].data.is_some());
1063 assert_eq!(
1064 events[0].data,
1065 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1066 );
1067 responder.send().expect("injector stream failed to respond.");
1068 }
1069 other => panic!("Received unexpected value: {:?}", other),
1070 }
1071
1072 let event_time = zx::MonotonicInstant::get();
1074 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1075 let descriptor = get_touch_screen_device_descriptor();
1076 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1077 hashmap! {
1078 fidl_ui_input::PointerEventPhase::Add
1079 => vec![contact.clone()],
1080 },
1081 event_time,
1082 &descriptor,
1083 ))
1084 .unwrap();
1085
1086 let handle_event_fut =
1088 fixtures.touch_handler.clone().handle_unhandled_input_event(input_event);
1089
1090 let expected_event = create_touch_pointer_sample_event(
1092 pointerinjector::EventPhase::Add,
1093 &contact,
1094 Position { x: 20.0, y: 40.0 },
1095 event_time,
1096 );
1097
1098 let device_fut =
1101 handle_device_request_stream(injector_device_request_stream, expected_event);
1102 let (handle_result, _) = futures::future::join(handle_event_fut, device_fut).await;
1103
1104 assert_matches!(
1106 handle_result.as_slice(),
1107 [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1108 );
1109 }
1110
1111 #[fuchsia::test]
1113 async fn add_touchpad_contact_with_viewport() {
1114 let mut fixtures = TestFixtures::new().await;
1115
1116 let (injector_device_proxy, mut injector_device_request_stream) =
1118 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1119 fixtures
1120 .touch_handler
1121 .mutable_state
1122 .borrow_mut()
1123 .injectors
1124 .insert(1, injector_device_proxy);
1125
1126 let _watch_viewport_task =
1128 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1129
1130 match fixtures.configuration_request_stream.next().await {
1132 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1133 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1134 }
1135 other => panic!("Received unexpected value: {:?}", other),
1136 };
1137
1138 match injector_device_request_stream.next().await {
1140 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
1141 assert_eq!(events.len(), 1);
1142 assert!(events[0].data.is_some());
1143 assert_eq!(
1144 events[0].data,
1145 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1146 );
1147 responder.send().expect("injector stream failed to respond.");
1148 }
1149 other => panic!("Received unexpected value: {:?}", other),
1150 }
1151
1152 let event_time = zx::MonotonicInstant::get();
1154 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1155 let descriptor = get_touchpad_device_descriptor();
1156 let input_event = input_device::UnhandledInputEvent::try_from(create_touchpad_event(
1157 vec![contact.clone()],
1158 HashSet::new(),
1159 event_time,
1160 &descriptor,
1161 ))
1162 .unwrap();
1163
1164 let handle_event_fut =
1166 fixtures.touch_handler.clone().handle_unhandled_input_event(input_event);
1167
1168 let handle_result = handle_event_fut.await;
1169
1170 assert_matches!(
1172 handle_result.as_slice(),
1173 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
1174 );
1175
1176 assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1178 }
1179
1180 #[fuchsia::test(allow_stalls = false)]
1181 async fn touch_injector_handler_initialized_with_inspect_node() {
1182 let fixtures = TestFixtures::new().await;
1183 diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1184 test_node: {
1185 touch_injector_handler: {
1186 events_received_count: 0u64,
1187 events_handled_count: 0u64,
1188 last_received_timestamp_ns: 0u64,
1189 "fuchsia.inspect.Health": {
1190 status: "STARTING_UP",
1191 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1194 },
1195 }
1196 }
1197 });
1198 }
1199
1200 #[fuchsia::test(allow_stalls = false)]
1201 async fn touch_injector_handler_inspect_counts_events() {
1202 let fixtures = TestFixtures::new().await;
1203
1204 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1205 let descriptor = get_touch_screen_device_descriptor();
1206 let event_time1 = zx::MonotonicInstant::get();
1207 let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1208 let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
1209
1210 let input_events = vec![
1211 create_touch_screen_event(
1212 hashmap! {
1213 fidl_ui_input::PointerEventPhase::Add
1214 => vec![contact.clone()],
1215 },
1216 event_time1,
1217 &descriptor,
1218 ),
1219 create_touch_screen_event(
1220 hashmap! {
1221 fidl_ui_input::PointerEventPhase::Move
1222 => vec![contact.clone()],
1223 },
1224 event_time2,
1225 &descriptor,
1226 ),
1227 create_fake_input_event(event_time2),
1229 create_touch_screen_event_with_handled(
1231 hashmap! {
1232 fidl_ui_input::PointerEventPhase::Move
1233 => vec![contact.clone()],
1234 },
1235 event_time2,
1236 &descriptor,
1237 input_device::Handled::Yes,
1238 ),
1239 create_touch_screen_event(
1240 hashmap! {
1241 fidl_ui_input::PointerEventPhase::Remove
1242 => vec![contact.clone()],
1243 },
1244 event_time3,
1245 &descriptor,
1246 ),
1247 ];
1248
1249 for input_event in input_events {
1250 fixtures.touch_handler.clone().handle_input_event(input_event).await;
1251 }
1252
1253 let last_received_event_time: u64 = event_time3.into_nanos().try_into().unwrap();
1254
1255 diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1256 test_node: {
1257 touch_injector_handler: {
1258 events_received_count: 3u64,
1259 events_handled_count: 3u64,
1260 last_received_timestamp_ns: last_received_event_time,
1261 "fuchsia.inspect.Health": {
1262 status: "STARTING_UP",
1263 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1266 },
1267 }
1268 }
1269 });
1270 }
1271}