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