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