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