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 mut unhandled_input_event: input_device::UnhandledInputEvent,
85 ) -> Vec<input_device::InputEvent> {
86 fuchsia_trace::duration!(c"input", c"touch_injector_handler");
87 match unhandled_input_event {
88 input_device::UnhandledInputEvent {
89 device_event: input_device::InputDeviceEvent::TouchScreen(ref mut 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[processing]");
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 std::mem::drop(touch_buttons_event.wake_lease.take());
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: &mut 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 touch_event.wake_lease.take(),
371 )
372 });
373 events.extend(new_events);
374 }
375
376 let injector =
377 self.mutable_state.borrow().injectors.get(&touch_descriptor.device_id).cloned();
378 if let Some(injector) = injector {
379 let fut = injector.inject(events);
380 fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
382 let _ = fut.await;
383 Ok(())
384 } else {
385 fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
386 Err(anyhow::format_err!(
387 "No injector found for touch device {}.",
388 touch_descriptor.device_id
389 ))
390 }
391 }
392
393 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 wake_lease: Option<zx::EventPair>,
409 ) -> pointerinjector::Event {
410 let position =
411 Self::display_coordinate_from_contact(&contact, &touch_descriptor, display_size);
412 let pointer_sample = pointerinjector::PointerSample {
413 pointer_id: Some(contact.id),
414 phase: Some(phase),
415 position_in_viewport: Some([position.x, position.y]),
416 scroll_v: None,
417 scroll_h: None,
418 pressed_buttons: None,
419 ..Default::default()
420 };
421 let data = pointerinjector::Data::PointerSample(pointer_sample);
422
423 let trace_flow_id = fuchsia_trace::Id::random();
424 let event = pointerinjector::Event {
425 timestamp: Some(event_time.into_nanos()),
426 data: Some(data),
427 trace_flow_id: Some(trace_flow_id.into()),
428 wake_lease,
429 ..Default::default()
430 };
431
432 fuchsia_trace::flow_begin!(c"input", c"dispatch_event_to_scenic", trace_flow_id);
433
434 event
435 }
436
437 fn display_coordinate_from_contact(
451 contact: &touch_binding::TouchContact,
452 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
453 display_size: &Size,
454 ) -> Position {
455 if let Some(contact_descriptor) = touch_descriptor.contacts.first() {
456 let x_range: f32 =
458 contact_descriptor.x_range.max as f32 - contact_descriptor.x_range.min as f32;
459 let x_wrt_range: f32 = contact.position.x - contact_descriptor.x_range.min as f32;
460 let x: f32 = (display_size.width * x_wrt_range) / x_range;
461
462 let y_range: f32 =
464 contact_descriptor.y_range.max as f32 - contact_descriptor.y_range.min as f32;
465 let y_wrt_range: f32 = contact.position.y - contact_descriptor.y_range.min as f32;
466 let y: f32 = (display_size.height * y_wrt_range) / y_range;
467
468 Position { x, y }
469 } else {
470 return contact.position;
471 }
472 }
473
474 pub async fn watch_viewport(self: Rc<Self>) {
476 let configuration_proxy = self.configuration_proxy.clone();
477 let mut viewport_stream = HangingGetStream::new(
478 configuration_proxy,
479 pointerinjector_config::SetupProxy::watch_viewport,
480 );
481 loop {
482 match viewport_stream.next().await {
483 Some(Ok(new_viewport)) => {
484 self.mutable_state.borrow_mut().viewport = Some(new_viewport.clone());
486
487 let injectors: Vec<pointerinjector::DeviceProxy> =
489 self.mutable_state.borrow_mut().injectors.values().cloned().collect();
490 for injector in injectors {
491 let events = vec![pointerinjector::Event {
492 timestamp: Some(fuchsia_async::MonotonicInstant::now().into_nanos()),
493 data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
494 trace_flow_id: Some(fuchsia_trace::Id::random().into()),
495 ..Default::default()
496 }];
497 injector.inject(events).await.expect("Failed to inject updated viewport.");
498 }
499 }
500 Some(Err(e)) => {
501 self.metrics_logger.log_error(
502 InputPipelineErrorMetricDimensionEvent::TouchInjectorErrorWhileReadingViewportUpdate,
503 std::format!("Error while reading viewport update: {}", e));
504 return;
505 }
506 None => {
507 self.metrics_logger.log_error(
508 InputPipelineErrorMetricDimensionEvent::TouchInjectorViewportUpdateStreamTerminatedUnexpectedly,
509 "Viewport update stream terminated unexpectedly");
510 return;
511 }
512 }
513 }
514 }
515
516 fn create_touch_buttons_event(
523 event: &mut touch_binding::TouchScreenEvent,
524 event_time: zx::MonotonicInstant,
525 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
526 ) -> fidl_ui_input::TouchButtonsEvent {
527 let pressed_buttons = match event.pressed_buttons.len() {
528 0 => None,
529 _ => Some(
530 event
531 .pressed_buttons
532 .clone()
533 .into_iter()
534 .map(|button| match button {
535 fidl_input_report::TouchButton::Palm => fidl_ui_input::TouchButton::Palm,
536 fidl_input_report::TouchButton::__SourceBreaking { unknown_ordinal: n } => {
537 fidl_ui_input::TouchButton::__SourceBreaking {
538 unknown_ordinal: n as u32,
539 }
540 }
541 })
542 .collect::<Vec<_>>(),
543 ),
544 };
545 fidl_ui_input::TouchButtonsEvent {
546 event_time: Some(event_time),
547 device_info: Some(fidl_ui_input::TouchDeviceInfo {
548 id: Some(touch_descriptor.device_id),
549 ..Default::default()
550 }),
551 pressed_buttons,
552 wake_lease: event.wake_lease.take(),
553 ..Default::default()
554 }
555 }
556
557 async fn send_event_to_listeners(self: &Rc<Self>, event: &fidl_ui_input::TouchButtonsEvent) {
562 let tracker = &self.mutable_state.borrow().send_event_task_tracker;
563
564 for (handle, listener) in &self.mutable_state.borrow().listeners {
565 let weak_handler = Rc::downgrade(&self);
566 let listener_clone = listener.clone();
567 let handle_clone = handle.clone();
568 let event_to_send = Self::clone_event(event);
569 let fut = async move {
570 match listener_clone.on_event(event_to_send).await {
571 Ok(_) => {}
572 Err(e) => {
573 if let Some(handler) = weak_handler.upgrade() {
574 handler.mutable_state.borrow_mut().listeners.remove(&handle_clone);
575 log::info!(
576 "Unregistering listener; unable to send TouchButtonsEvent: {:?}",
577 e
578 )
579 }
580 }
581 }
582 };
583
584 let metrics_logger_clone = self.metrics_logger.clone();
585 tracker.track(metrics_logger_clone, fasync::Task::local(fut));
586 }
587 }
588
589 pub async fn register_listener_proxy(
594 self: &Rc<Self>,
595 proxy: fidl_ui_policy::TouchButtonsListenerProxy,
596 ) {
597 self.mutable_state
598 .borrow_mut()
599 .listeners
600 .insert(proxy.as_channel().raw_handle(), proxy.clone());
601
602 if let Some(event) = &self.mutable_state.borrow().last_button_event {
604 let event_to_send = Self::clone_event(event);
605 let fut = async move {
606 match proxy.on_event(event_to_send).await {
607 Ok(_) => {}
608 Err(e) => {
609 log::info!("Failed to send touch buttons event to listener {:?}", e)
610 }
611 }
612 };
613 let metrics_logger_clone = self.metrics_logger.clone();
614 self.mutable_state
615 .borrow()
616 .send_event_task_tracker
617 .track(metrics_logger_clone, fasync::Task::local(fut));
618 }
619 }
620}
621
622#[derive(Debug)]
625pub struct LocalTaskTracker {
626 sender: mpsc::UnboundedSender<fasync::Task<()>>,
627 _receiver_task: fasync::Task<()>,
628}
629
630impl LocalTaskTracker {
631 pub fn new() -> Self {
632 let (sender, receiver) = mpsc::unbounded();
633 let receiver_task = fasync::Task::local(async move {
634 receiver.for_each_concurrent(None, |task: fasync::Task<()>| task).await
636 });
637
638 Self { sender, _receiver_task: receiver_task }
639 }
640
641 pub fn track(&self, metrics_logger: metrics::MetricsLogger, task: fasync::Task<()>) {
643 match self.sender.unbounded_send(task) {
644 Ok(_) => {}
645 Err(e) => {
649 metrics_logger.log_error(
650 InputPipelineErrorMetricDimensionEvent::TouchFailedToSendTouchScreenEvent,
651 std::format!("Unexpected {e:?} while pushing task"),
652 );
653 }
654 };
655 }
656}
657
658#[cfg(test)]
659mod tests {
660 use super::*;
661 use crate::input_handler::InputHandler;
662 use crate::testing_utilities::{
663 create_fake_input_event, create_touch_contact, create_touch_pointer_sample_event,
664 create_touch_screen_event, create_touch_screen_event_with_handled, create_touchpad_event,
665 get_touch_screen_device_descriptor,
666 };
667 use assert_matches::assert_matches;
668 use futures::{FutureExt, TryStreamExt};
669 use maplit::hashmap;
670 use pretty_assertions::assert_eq;
671 use std::collections::HashSet;
672 use std::convert::TryFrom as _;
673 use std::ops::Add;
674 use {
675 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
676 fidl_fuchsia_ui_policy as fidl_ui_policy, fuchsia_async as fasync,
677 };
678
679 const TOUCH_ID: u32 = 1;
680 const DISPLAY_WIDTH: f32 = 100.0;
681 const DISPLAY_HEIGHT: f32 = 100.0;
682
683 struct TestFixtures {
684 touch_handler: Rc<TouchInjectorHandler>,
685 device_listener_proxy: fidl_ui_policy::DeviceListenerRegistryProxy,
686 injector_registry_request_stream: pointerinjector::RegistryRequestStream,
687 configuration_request_stream: pointerinjector_config::SetupRequestStream,
688 inspector: fuchsia_inspect::Inspector,
689 _test_node: fuchsia_inspect::Node,
690 }
691
692 fn spawn_device_listener_registry_server(
693 handler: Rc<TouchInjectorHandler>,
694 ) -> fidl_ui_policy::DeviceListenerRegistryProxy {
695 let (device_listener_proxy, mut device_listener_stream) =
696 fidl::endpoints::create_proxy_and_stream::<fidl_ui_policy::DeviceListenerRegistryMarker>(
697 );
698
699 fasync::Task::local(async move {
700 loop {
701 match device_listener_stream.try_next().await {
702 Ok(Some(
703 fidl_ui_policy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
704 listener,
705 responder,
706 },
707 )) => {
708 handler.register_listener_proxy(listener.into_proxy()).await;
709 let _ = responder.send();
710 }
711 Ok(Some(_)) => {
712 panic!("Unexpected registration");
713 }
714 Ok(None) => {
715 break;
716 }
717 Err(e) => {
718 panic!("Error handling device listener registry request stream: {}", e);
719 }
720 }
721 }
722 })
723 .detach();
724
725 device_listener_proxy
726 }
727
728 impl TestFixtures {
729 async fn new() -> Self {
730 let inspector = fuchsia_inspect::Inspector::default();
731 let test_node = inspector.root().create_child("test_node");
732 let (configuration_proxy, mut configuration_request_stream) =
733 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
734 let (injector_registry_proxy, injector_registry_request_stream) =
735 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
736
737 let touch_handler_fut = TouchInjectorHandler::new_handler(
738 configuration_proxy,
739 injector_registry_proxy,
740 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
741 &test_node,
742 metrics::MetricsLogger::default(),
743 );
744
745 let handle_initial_request_fut = async {
746 match configuration_request_stream.next().await {
747 Some(Ok(pointerinjector_config::SetupRequest::GetViewRefs {
748 responder,
749 ..
750 })) => {
751 let context = fuchsia_scenic::ViewRefPair::new()
752 .expect("Failed to create viewrefpair.")
753 .view_ref;
754 let target = fuchsia_scenic::ViewRefPair::new()
755 .expect("Failed to create viewrefpair.")
756 .view_ref;
757 let _ = responder.send(context, target);
758 }
759 other => panic!("Expected GetViewRefs request, got {:?}", other),
760 }
761 };
762
763 let (touch_handler_res, _) =
764 futures::future::join(touch_handler_fut, handle_initial_request_fut).await;
765
766 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
767 let device_listener_proxy =
768 spawn_device_listener_registry_server(touch_handler.clone());
769
770 TestFixtures {
771 touch_handler,
772 device_listener_proxy,
773 injector_registry_request_stream,
774 configuration_request_stream,
775 inspector,
776 _test_node: test_node,
777 }
778 }
779 }
780
781 fn get_touchpad_device_descriptor() -> input_device::InputDeviceDescriptor {
783 input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
784 device_id: 1,
785 contacts: vec![touch_binding::ContactDeviceDescriptor {
786 x_range: fidl_input_report::Range { min: 0, max: 100 },
787 y_range: fidl_input_report::Range { min: 0, max: 100 },
788 x_unit: fidl_input_report::Unit {
789 type_: fidl_input_report::UnitType::Meters,
790 exponent: -6,
791 },
792 y_unit: fidl_input_report::Unit {
793 type_: fidl_input_report::UnitType::Meters,
794 exponent: -6,
795 },
796 pressure_range: None,
797 width_range: None,
798 height_range: None,
799 }],
800 })
801 }
802
803 async fn handle_device_request_stream(
806 mut injector_stream: pointerinjector::DeviceRequestStream,
807 expected_event: pointerinjector::Event,
808 ) {
809 match injector_stream.next().await {
810 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
811 assert_eq!(events.len(), 1);
812 assert_eq!(events[0].timestamp, expected_event.timestamp);
813 assert_eq!(events[0].data, expected_event.data);
814 responder.send().expect("failed to respond");
815 }
816 Some(Err(e)) => panic!("FIDL error {}", e),
817 None => panic!("Expected another event."),
818 }
819 }
820
821 fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
823 pointerinjector::Viewport {
824 extents: Some([[min, min], [max, max]]),
825 viewport_to_context_transform: None,
826 ..Default::default()
827 }
828 }
829
830 #[fuchsia::test]
831 async fn events_with_pressed_buttons_are_sent_to_listener() {
832 let fixtures = TestFixtures::new().await;
833 let (listener, mut listener_stream) =
834 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
835 fixtures
836 .device_listener_proxy
837 .register_touch_buttons_listener(listener)
838 .await
839 .expect("Failed to register listener.");
840
841 let descriptor = get_touch_screen_device_descriptor();
842 let event_time = zx::MonotonicInstant::get();
843 let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
844
845 let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
846
847 let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
848 event_time: Some(event_time),
849 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
850 ..Default::default()
851 };
852
853 assert_matches!(
854 listener_stream.next().await,
855 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
856 event,
857 responder,
858 })) => {
859 assert_eq!(event, expected_touch_buttons_event);
860 let _ = responder.send();
861 }
862 );
863 }
864
865 #[fuchsia::test]
866 async fn events_with_contacts_are_not_sent_to_listener() {
867 let fixtures = TestFixtures::new().await;
868 let (listener, mut listener_stream) =
869 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
870 fixtures
871 .device_listener_proxy
872 .register_touch_buttons_listener(listener)
873 .await
874 .expect("Failed to register listener.");
875
876 let descriptor = get_touch_screen_device_descriptor();
877 let event_time = zx::MonotonicInstant::get();
878 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
879 let input_event = create_touch_screen_event(
880 hashmap! {
881 fidl_ui_input::PointerEventPhase::Add
882 => vec![contact.clone()],
883 },
884 event_time,
885 &descriptor,
886 );
887
888 let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
889
890 assert!(listener_stream.next().now_or_never().is_none());
891 }
892
893 #[fuchsia::test]
894 async fn multiple_listeners_receive_pressed_button_events() {
895 let fixtures = TestFixtures::new().await;
896 let (first_listener, mut first_listener_stream) =
897 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
898 let (second_listener, mut second_listener_stream) =
899 fidl::endpoints::create_request_stream::<fidl_ui_policy::TouchButtonsListenerMarker>();
900 fixtures
901 .device_listener_proxy
902 .register_touch_buttons_listener(first_listener)
903 .await
904 .expect("Failed to register listener.");
905 fixtures
906 .device_listener_proxy
907 .register_touch_buttons_listener(second_listener)
908 .await
909 .expect("Failed to register listener.");
910
911 let descriptor = get_touch_screen_device_descriptor();
912 let event_time = zx::MonotonicInstant::get();
913 let input_event = create_touch_screen_event(hashmap! {}, event_time, &descriptor);
914
915 let _ = fixtures.touch_handler.clone().handle_input_event(input_event).await;
916
917 let expected_touch_buttons_event = fidl_ui_input::TouchButtonsEvent {
918 event_time: Some(event_time),
919 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
920 ..Default::default()
921 };
922
923 assert_matches!(
924 first_listener_stream.next().await,
925 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
926 event,
927 responder,
928 })) => {
929 assert_eq!(event, expected_touch_buttons_event);
930 let _ = responder.send();
931 }
932 );
933 assert_matches!(
934 second_listener_stream.next().await,
935 Some(Ok(fidl_ui_policy::TouchButtonsListenerRequest::OnEvent {
936 event,
937 responder,
938 })) => {
939 assert_eq!(event, expected_touch_buttons_event);
940 let _ = responder.send();
941 }
942 );
943 }
944
945 #[fuchsia::test]
948 async fn receives_viewport_updates() {
949 let mut fixtures = TestFixtures::new().await;
950
951 let (injector_device_proxy, mut injector_device_request_stream) =
953 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
954 fixtures
955 .touch_handler
956 .mutable_state
957 .borrow_mut()
958 .injectors
959 .insert(1, injector_device_proxy);
960
961 {
963 let _watch_viewport_task =
965 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
966
967 match fixtures.configuration_request_stream.next().await {
969 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
970 responder, ..
971 })) => {
972 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
973 }
974 other => panic!("Received unexpected value: {:?}", other),
975 };
976
977 match injector_device_request_stream.next().await {
979 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
980 assert_eq!(events.len(), 1);
981 assert!(events[0].data.is_some());
982 assert_eq!(
983 events[0].data,
984 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
985 );
986 responder.send().expect("injector stream failed to respond.");
987 }
988 other => panic!("Received unexpected value: {:?}", other),
989 }
990
991 match fixtures.configuration_request_stream.next().await {
994 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
995 responder, ..
996 })) => {
997 responder
998 .send(&create_viewport(100.0, 200.0))
999 .expect("Failed to send viewport.");
1000 }
1001 other => panic!("Received unexpected value: {:?}", other),
1002 };
1003
1004 match injector_device_request_stream.next().await {
1006 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
1007 assert_eq!(events.len(), 1);
1008 assert!(events[0].data.is_some());
1009 assert_eq!(
1010 events[0].data,
1011 Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
1012 );
1013 responder.send().expect("injector stream failed to respond.");
1014 }
1015 other => panic!("Received unexpected value: {:?}", other),
1016 }
1017 }
1018
1019 let expected_viewport = create_viewport(100.0, 200.0);
1021 assert_eq!(fixtures.touch_handler.mutable_state.borrow().viewport, Some(expected_viewport));
1022 }
1023
1024 #[fuchsia::test]
1026 async fn add_contact_drops_without_viewport() {
1027 let mut fixtures = TestFixtures::new().await;
1028
1029 let event_time = zx::MonotonicInstant::get();
1031 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1032 let descriptor = get_touch_screen_device_descriptor();
1033 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1034 hashmap! {
1035 fidl_ui_input::PointerEventPhase::Add
1036 => vec![contact.clone()],
1037 },
1038 event_time,
1039 &descriptor,
1040 ))
1041 .unwrap();
1042
1043 fixtures.touch_handler.mutable_state.borrow_mut().viewport = None;
1045
1046 let _ = fixtures.touch_handler.clone().handle_unhandled_input_event(input_event).await;
1048
1049 assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1051 }
1052
1053 #[fuchsia::test]
1055 async fn add_contact_succeeds_with_viewport() {
1056 let mut fixtures = TestFixtures::new().await;
1057
1058 let (injector_device_proxy, mut injector_device_request_stream) =
1060 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1061 fixtures
1062 .touch_handler
1063 .mutable_state
1064 .borrow_mut()
1065 .injectors
1066 .insert(1, injector_device_proxy);
1067
1068 let _watch_viewport_task =
1070 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1071
1072 match fixtures.configuration_request_stream.next().await {
1074 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1075 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1076 }
1077 other => panic!("Received unexpected value: {:?}", other),
1078 };
1079
1080 match injector_device_request_stream.next().await {
1082 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
1083 assert_eq!(events.len(), 1);
1084 assert!(events[0].data.is_some());
1085 assert_eq!(
1086 events[0].data,
1087 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1088 );
1089 responder.send().expect("injector stream failed to respond.");
1090 }
1091 other => panic!("Received unexpected value: {:?}", other),
1092 }
1093
1094 let event_time = zx::MonotonicInstant::get();
1096 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1097 let descriptor = get_touch_screen_device_descriptor();
1098 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
1099 hashmap! {
1100 fidl_ui_input::PointerEventPhase::Add
1101 => vec![contact.clone()],
1102 },
1103 event_time,
1104 &descriptor,
1105 ))
1106 .unwrap();
1107
1108 let handle_event_fut =
1110 fixtures.touch_handler.clone().handle_unhandled_input_event(input_event);
1111
1112 let expected_event = create_touch_pointer_sample_event(
1114 pointerinjector::EventPhase::Add,
1115 &contact,
1116 Position { x: 20.0, y: 40.0 },
1117 event_time,
1118 );
1119
1120 let device_fut =
1123 handle_device_request_stream(injector_device_request_stream, expected_event);
1124 let (handle_result, _) = futures::future::join(handle_event_fut, device_fut).await;
1125
1126 assert_matches!(
1128 handle_result.as_slice(),
1129 [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
1130 );
1131 }
1132
1133 #[fuchsia::test]
1135 async fn add_touchpad_contact_with_viewport() {
1136 let mut fixtures = TestFixtures::new().await;
1137
1138 let (injector_device_proxy, mut injector_device_request_stream) =
1140 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
1141 fixtures
1142 .touch_handler
1143 .mutable_state
1144 .borrow_mut()
1145 .injectors
1146 .insert(1, injector_device_proxy);
1147
1148 let _watch_viewport_task =
1150 fasync::Task::local(fixtures.touch_handler.clone().watch_viewport());
1151
1152 match fixtures.configuration_request_stream.next().await {
1154 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
1155 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
1156 }
1157 other => panic!("Received unexpected value: {:?}", other),
1158 };
1159
1160 match injector_device_request_stream.next().await {
1162 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
1163 assert_eq!(events.len(), 1);
1164 assert!(events[0].data.is_some());
1165 assert_eq!(
1166 events[0].data,
1167 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
1168 );
1169 responder.send().expect("injector stream failed to respond.");
1170 }
1171 other => panic!("Received unexpected value: {:?}", other),
1172 }
1173
1174 let event_time = zx::MonotonicInstant::get();
1176 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1177 let descriptor = get_touchpad_device_descriptor();
1178 let input_event = input_device::UnhandledInputEvent::try_from(create_touchpad_event(
1179 vec![contact.clone()],
1180 HashSet::new(),
1181 event_time,
1182 &descriptor,
1183 ))
1184 .unwrap();
1185
1186 let handle_event_fut =
1188 fixtures.touch_handler.clone().handle_unhandled_input_event(input_event);
1189
1190 let handle_result = handle_event_fut.await;
1191
1192 assert_matches!(
1194 handle_result.as_slice(),
1195 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
1196 );
1197
1198 assert!(fixtures.injector_registry_request_stream.next().now_or_never().is_none());
1200 }
1201
1202 #[fuchsia::test(allow_stalls = false)]
1203 async fn touch_injector_handler_initialized_with_inspect_node() {
1204 let fixtures = TestFixtures::new().await;
1205 diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1206 test_node: {
1207 touch_injector_handler: {
1208 events_received_count: 0u64,
1209 events_handled_count: 0u64,
1210 last_received_timestamp_ns: 0u64,
1211 "fuchsia.inspect.Health": {
1212 status: "STARTING_UP",
1213 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1216 },
1217 }
1218 }
1219 });
1220 }
1221
1222 #[fuchsia::test(allow_stalls = false)]
1223 async fn touch_injector_handler_inspect_counts_events() {
1224 let fixtures = TestFixtures::new().await;
1225
1226 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
1227 let descriptor = get_touch_screen_device_descriptor();
1228 let event_time1 = zx::MonotonicInstant::get();
1229 let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
1230 let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
1231
1232 let input_events = vec![
1233 create_touch_screen_event(
1234 hashmap! {
1235 fidl_ui_input::PointerEventPhase::Add
1236 => vec![contact.clone()],
1237 },
1238 event_time1,
1239 &descriptor,
1240 ),
1241 create_touch_screen_event(
1242 hashmap! {
1243 fidl_ui_input::PointerEventPhase::Move
1244 => vec![contact.clone()],
1245 },
1246 event_time2,
1247 &descriptor,
1248 ),
1249 create_fake_input_event(event_time2),
1251 create_touch_screen_event_with_handled(
1253 hashmap! {
1254 fidl_ui_input::PointerEventPhase::Move
1255 => vec![contact.clone()],
1256 },
1257 event_time2,
1258 &descriptor,
1259 input_device::Handled::Yes,
1260 ),
1261 create_touch_screen_event(
1262 hashmap! {
1263 fidl_ui_input::PointerEventPhase::Remove
1264 => vec![contact.clone()],
1265 },
1266 event_time3,
1267 &descriptor,
1268 ),
1269 ];
1270
1271 for input_event in input_events {
1272 fixtures.touch_handler.clone().handle_input_event(input_event).await;
1273 }
1274
1275 let last_received_event_time: u64 = event_time3.into_nanos().try_into().unwrap();
1276
1277 diagnostics_assertions::assert_data_tree!(fixtures.inspector, root: {
1278 test_node: {
1279 touch_injector_handler: {
1280 events_received_count: 3u64,
1281 events_handled_count: 3u64,
1282 last_received_timestamp_ns: last_received_event_time,
1283 "fuchsia.inspect.Health": {
1284 status: "STARTING_UP",
1285 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1288 },
1289 }
1290 }
1291 });
1292 }
1293
1294 #[fuchsia::test]
1295 async fn clone_event_with_lease_duplicates_lease() {
1296 let (event_pair, _) = fidl::EventPair::create();
1297 let event = fidl_ui_input::TouchButtonsEvent {
1298 event_time: Some(zx::MonotonicInstant::from_nanos(1)),
1299 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
1300 pressed_buttons: Some(vec![fidl_ui_input::TouchButton::Palm]),
1301 wake_lease: Some(event_pair),
1302 ..Default::default()
1303 };
1304 let cloned_event = TouchInjectorHandler::clone_event(&event);
1305 assert_eq!(event.event_time, cloned_event.event_time);
1306 assert_eq!(event.device_info, cloned_event.device_info);
1307 assert_eq!(event.pressed_buttons, cloned_event.pressed_buttons);
1308 assert!(event.wake_lease.is_some());
1309 assert!(cloned_event.wake_lease.is_some());
1310 assert_ne!(
1311 event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle(),
1312 cloned_event.wake_lease.as_ref().unwrap().as_handle_ref().raw_handle()
1313 );
1314 }
1315
1316 #[fuchsia::test]
1317 async fn clone_event_without_lease_has_no_lease() {
1318 let event = fidl_ui_input::TouchButtonsEvent {
1319 event_time: Some(zx::MonotonicInstant::from_nanos(1)),
1320 device_info: Some(fidl_ui_input::TouchDeviceInfo { id: Some(1), ..Default::default() }),
1321 pressed_buttons: Some(vec![fidl_ui_input::TouchButton::Palm]),
1322 wake_lease: None,
1323 ..Default::default()
1324 };
1325 let cloned_event = TouchInjectorHandler::clone_event(&event);
1326 assert_eq!(event.event_time, cloned_event.event_time);
1327 assert_eq!(event.device_info, cloned_event.device_info);
1328 assert_eq!(event.pressed_buttons, cloned_event.pressed_buttons);
1329 assert!(event.wake_lease.is_none());
1330 assert!(cloned_event.wake_lease.is_none());
1331 }
1332}