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