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