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::create_proxy;
13use fuchsia_component::client::connect_to_protocol;
14use fuchsia_inspect::health::Reporter;
15use futures::stream::StreamExt;
16use metrics_registry::*;
17use std::cell::RefCell;
18use std::collections::HashMap;
19use std::rc::Rc;
20use {
21 fidl_fuchsia_ui_pointerinjector as pointerinjector,
22 fidl_fuchsia_ui_pointerinjector_configuration as pointerinjector_config,
23};
24
25pub struct TouchInjectorHandler {
28 mutable_state: RefCell<MutableState>,
30
31 context_view_ref: fidl_fuchsia_ui_views::ViewRef,
34
35 target_view_ref: fidl_fuchsia_ui_views::ViewRef,
38
39 display_size: Size,
43
44 injector_registry_proxy: pointerinjector::RegistryProxy,
46
47 configuration_proxy: pointerinjector_config::SetupProxy,
49
50 pub inspect_status: InputHandlerStatus,
52
53 metrics_logger: metrics::MetricsLogger,
55}
56
57#[derive(Debug)]
58struct MutableState {
59 viewport: Option<pointerinjector::Viewport>,
62
63 injectors: HashMap<u32, pointerinjector::DeviceProxy>,
65}
66
67#[async_trait(?Send)]
68impl UnhandledInputHandler for TouchInjectorHandler {
69 async fn handle_unhandled_input_event(
70 self: Rc<Self>,
71 unhandled_input_event: input_device::UnhandledInputEvent,
72 ) -> Vec<input_device::InputEvent> {
73 fuchsia_trace::duration!(c"input", c"presentation_on_event");
74 match unhandled_input_event {
75 input_device::UnhandledInputEvent {
76 device_event: input_device::InputDeviceEvent::TouchScreen(ref touch_event),
77 device_descriptor:
78 input_device::InputDeviceDescriptor::TouchScreen(ref touch_device_descriptor),
79 event_time,
80 trace_id,
81 } => {
82 self.inspect_status.count_received_event(input_device::InputEvent::from(
83 unhandled_input_event.clone(),
84 ));
85 fuchsia_trace::duration!(c"input", c"touch_injector_handler");
86 if let Some(trace_id) = trace_id {
87 fuchsia_trace::flow_end!(c"input", c"event_in_input_pipeline", trace_id.into());
88 }
89 if let Err(e) = self.ensure_injector_registered(&touch_device_descriptor).await {
91 self.metrics_logger.log_error(
92 InputPipelineErrorMetricDimensionEvent::TouchInjectorEnsureInjectorRegisteredFailed,
93 std::format!("ensure_injector_registered failed: {}", e));
94 }
95
96 if let Err(e) = self
98 .send_event_to_scenic(&touch_event, &touch_device_descriptor, event_time)
99 .await
100 {
101 self.metrics_logger.log_error(
102 InputPipelineErrorMetricDimensionEvent::TouchInjectorSendEventToScenicFailed,
103 std::format!("send_event_to_scenic failed: {}", e));
104 }
105
106 self.inspect_status.count_handled_event();
108 vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
109 }
110 _ => vec![input_device::InputEvent::from(unhandled_input_event)],
111 }
112 }
113
114 fn set_handler_healthy(self: std::rc::Rc<Self>) {
115 self.inspect_status.health_node.borrow_mut().set_ok();
116 }
117
118 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
119 self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
120 }
121}
122
123impl TouchInjectorHandler {
124 pub async fn new(
136 display_size: Size,
137 input_handlers_node: &fuchsia_inspect::Node,
138 metrics_logger: metrics::MetricsLogger,
139 ) -> Result<Rc<Self>, Error> {
140 let configuration_proxy = connect_to_protocol::<pointerinjector_config::SetupMarker>()?;
141 let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
142
143 Self::new_handler(
144 configuration_proxy,
145 injector_registry_proxy,
146 display_size,
147 input_handlers_node,
148 metrics_logger,
149 )
150 .await
151 }
152
153 pub async fn new_with_config_proxy(
168 configuration_proxy: pointerinjector_config::SetupProxy,
169 display_size: Size,
170 input_handlers_node: &fuchsia_inspect::Node,
171 metrics_logger: metrics::MetricsLogger,
172 ) -> Result<Rc<Self>, Error> {
173 let injector_registry_proxy = connect_to_protocol::<pointerinjector::RegistryMarker>()?;
174 Self::new_handler(
175 configuration_proxy,
176 injector_registry_proxy,
177 display_size,
178 input_handlers_node,
179 metrics_logger,
180 )
181 .await
182 }
183
184 async fn new_handler(
200 configuration_proxy: pointerinjector_config::SetupProxy,
201 injector_registry_proxy: pointerinjector::RegistryProxy,
202 display_size: Size,
203 input_handlers_node: &fuchsia_inspect::Node,
204 metrics_logger: metrics::MetricsLogger,
205 ) -> Result<Rc<Self>, Error> {
206 let (context_view_ref, target_view_ref) = configuration_proxy.get_view_refs().await?;
208
209 let inspect_status = InputHandlerStatus::new(
210 input_handlers_node,
211 "touch_injector_handler",
212 false,
213 );
214 let handler = Rc::new(Self {
215 mutable_state: RefCell::new(MutableState { viewport: None, injectors: HashMap::new() }),
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 async fn ensure_injector_registered(
234 self: &Rc<Self>,
235 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
236 ) -> Result<(), anyhow::Error> {
237 if self.mutable_state.borrow().injectors.contains_key(&touch_descriptor.device_id) {
238 return Ok(());
239 }
240
241 let (device_proxy, device_server) = create_proxy::<pointerinjector::DeviceMarker>();
243 let context = fuchsia_scenic::duplicate_view_ref(&self.context_view_ref)
244 .context("Failed to duplicate context view ref.")?;
245 let target = fuchsia_scenic::duplicate_view_ref(&self.target_view_ref)
246 .context("Failed to duplicate target view ref.")?;
247 let viewport = self.mutable_state.borrow().viewport.clone();
248 if viewport.is_none() {
249 return Err(anyhow::format_err!(
252 "Received a touch event without a viewport to inject into."
253 ));
254 }
255 let config = pointerinjector::Config {
256 device_id: Some(touch_descriptor.device_id),
257 device_type: Some(pointerinjector::DeviceType::Touch),
258 context: Some(pointerinjector::Context::View(context)),
259 target: Some(pointerinjector::Target::View(target)),
260 viewport,
261 dispatch_policy: Some(pointerinjector::DispatchPolicy::TopHitAndAncestorsInTarget),
262 scroll_v_range: None,
263 scroll_h_range: None,
264 buttons: None,
265 ..Default::default()
266 };
267
268 self.mutable_state.borrow_mut().injectors.insert(touch_descriptor.device_id, device_proxy);
270
271 self.injector_registry_proxy
273 .register(config, device_server)
274 .await
275 .context("Failed to register injector.")?;
276 log::info!("Registered injector with device id {:?}", touch_descriptor.device_id);
277
278 Ok(())
279 }
280
281 async fn send_event_to_scenic(
288 &self,
289 touch_event: &touch_binding::TouchScreenEvent,
290 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
291 event_time: zx::MonotonicInstant,
292 ) -> Result<(), anyhow::Error> {
293 let ordered_phases = vec![
295 pointerinjector::EventPhase::Add,
296 pointerinjector::EventPhase::Change,
297 pointerinjector::EventPhase::Remove,
298 ];
299
300 fuchsia_trace::duration_begin!(c"input", c"touch-inject-into-scenic");
305
306 let mut events: Vec<pointerinjector::Event> = vec![];
307 for phase in ordered_phases {
308 let contacts: Vec<touch_binding::TouchContact> = touch_event
309 .injector_contacts
310 .get(&phase)
311 .map_or(vec![], |contacts| contacts.to_owned());
312 let new_events = contacts.into_iter().map(|contact| {
313 Self::create_pointer_sample_event(
314 phase,
315 &contact,
316 touch_descriptor,
317 &self.display_size,
318 event_time,
319 )
320 });
321 events.extend(new_events);
322 }
323
324 let injector =
325 self.mutable_state.borrow().injectors.get(&touch_descriptor.device_id).cloned();
326 if let Some(injector) = injector {
327 let fut = injector.inject(&events);
328 fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
330 let _ = fut.await;
331 Ok(())
332 } else {
333 fuchsia_trace::duration_end!(c"input", c"touch-inject-into-scenic");
334 Err(anyhow::format_err!(
335 "No injector found for touch device {}.",
336 touch_descriptor.device_id
337 ))
338 }
339 }
340
341 fn create_pointer_sample_event(
350 phase: pointerinjector::EventPhase,
351 contact: &touch_binding::TouchContact,
352 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
353 display_size: &Size,
354 event_time: zx::MonotonicInstant,
355 ) -> pointerinjector::Event {
356 let position =
357 Self::display_coordinate_from_contact(&contact, &touch_descriptor, display_size);
358 let pointer_sample = pointerinjector::PointerSample {
359 pointer_id: Some(contact.id),
360 phase: Some(phase),
361 position_in_viewport: Some([position.x, position.y]),
362 scroll_v: None,
363 scroll_h: None,
364 pressed_buttons: None,
365 ..Default::default()
366 };
367 let data = pointerinjector::Data::PointerSample(pointer_sample);
368
369 let trace_flow_id = fuchsia_trace::Id::random();
370 let event = pointerinjector::Event {
371 timestamp: Some(event_time.into_nanos()),
372 data: Some(data),
373 trace_flow_id: Some(trace_flow_id.into()),
374 ..Default::default()
375 };
376
377 fuchsia_trace::flow_begin!(c"input", c"dispatch_event_to_scenic", trace_flow_id);
378
379 event
380 }
381
382 fn display_coordinate_from_contact(
396 contact: &touch_binding::TouchContact,
397 touch_descriptor: &touch_binding::TouchScreenDeviceDescriptor,
398 display_size: &Size,
399 ) -> Position {
400 if let Some(contact_descriptor) = touch_descriptor.contacts.first() {
401 let x_range: f32 =
403 contact_descriptor.x_range.max as f32 - contact_descriptor.x_range.min as f32;
404 let x_wrt_range: f32 = contact.position.x - contact_descriptor.x_range.min as f32;
405 let x: f32 = (display_size.width * x_wrt_range) / x_range;
406
407 let y_range: f32 =
409 contact_descriptor.y_range.max as f32 - contact_descriptor.y_range.min as f32;
410 let y_wrt_range: f32 = contact.position.y - contact_descriptor.y_range.min as f32;
411 let y: f32 = (display_size.height * y_wrt_range) / y_range;
412
413 Position { x, y }
414 } else {
415 return contact.position;
416 }
417 }
418
419 pub async fn watch_viewport(self: Rc<Self>) {
421 let configuration_proxy = self.configuration_proxy.clone();
422 let mut viewport_stream = HangingGetStream::new(
423 configuration_proxy,
424 pointerinjector_config::SetupProxy::watch_viewport,
425 );
426 loop {
427 match viewport_stream.next().await {
428 Some(Ok(new_viewport)) => {
429 self.mutable_state.borrow_mut().viewport = Some(new_viewport.clone());
431
432 let injectors: Vec<pointerinjector::DeviceProxy> =
434 self.mutable_state.borrow_mut().injectors.values().cloned().collect();
435 for injector in injectors {
436 let events = &[pointerinjector::Event {
437 timestamp: Some(fuchsia_async::MonotonicInstant::now().into_nanos()),
438 data: Some(pointerinjector::Data::Viewport(new_viewport.clone())),
439 trace_flow_id: Some(fuchsia_trace::Id::random().into()),
440 ..Default::default()
441 }];
442 injector.inject(events).await.expect("Failed to inject updated viewport.");
443 }
444 }
445 Some(Err(e)) => {
446 self.metrics_logger.log_error(
447 InputPipelineErrorMetricDimensionEvent::TouchInjectorErrorWhileReadingViewportUpdate,
448 std::format!("Error while reading viewport update: {}", e));
449 return;
450 }
451 None => {
452 self.metrics_logger.log_error(
453 InputPipelineErrorMetricDimensionEvent::TouchInjectorViewportUpdateStreamTerminatedUnexpectedly,
454 "Viewport update stream terminated unexpectedly");
455 return;
456 }
457 }
458 }
459 }
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465 use crate::input_handler::InputHandler;
466 use crate::testing_utilities::{
467 create_fake_input_event, create_touch_contact, create_touch_pointer_sample_event,
468 create_touch_screen_event, create_touch_screen_event_with_handled, create_touchpad_event,
469 get_touch_screen_device_descriptor,
470 };
471 use assert_matches::assert_matches;
472 use maplit::hashmap;
473 use pretty_assertions::assert_eq;
474 use std::collections::HashSet;
475 use std::convert::TryFrom as _;
476 use std::ops::Add;
477 use {
478 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_ui_input as fidl_ui_input,
479 fuchsia_async as fasync,
480 };
481
482 const TOUCH_ID: u32 = 1;
483 const DISPLAY_WIDTH: f32 = 100.0;
484 const DISPLAY_HEIGHT: f32 = 100.0;
485
486 fn get_touchpad_device_descriptor() -> input_device::InputDeviceDescriptor {
488 input_device::InputDeviceDescriptor::Touchpad(touch_binding::TouchpadDeviceDescriptor {
489 device_id: 1,
490 contacts: vec![touch_binding::ContactDeviceDescriptor {
491 x_range: fidl_input_report::Range { min: 0, max: 100 },
492 y_range: fidl_input_report::Range { min: 0, max: 100 },
493 x_unit: fidl_input_report::Unit {
494 type_: fidl_input_report::UnitType::Meters,
495 exponent: -6,
496 },
497 y_unit: fidl_input_report::Unit {
498 type_: fidl_input_report::UnitType::Meters,
499 exponent: -6,
500 },
501 pressure_range: None,
502 width_range: None,
503 height_range: None,
504 }],
505 })
506 }
507
508 async fn handle_configuration_request_stream(
510 stream: &mut pointerinjector_config::SetupRequestStream,
511 ) {
512 if let Some(Ok(request)) = stream.next().await {
513 match request {
514 pointerinjector_config::SetupRequest::GetViewRefs { responder, .. } => {
515 let context = fuchsia_scenic::ViewRefPair::new()
516 .expect("Failed to create viewrefpair.")
517 .view_ref;
518 let target = fuchsia_scenic::ViewRefPair::new()
519 .expect("Failed to create viewrefpair.")
520 .view_ref;
521 let _ = responder.send(context, target);
522 }
523 _ => {}
524 };
525 }
526 }
527
528 async fn handle_device_request_stream(
531 mut injector_stream: pointerinjector::DeviceRequestStream,
532 expected_event: pointerinjector::Event,
533 ) {
534 match injector_stream.next().await {
535 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
536 assert_eq!(events.len(), 1);
537 assert_eq!(events[0].timestamp, expected_event.timestamp);
538 assert_eq!(events[0].data, expected_event.data);
539 responder.send().expect("failed to respond");
540 }
541 Some(Err(e)) => panic!("FIDL error {}", e),
542 None => panic!("Expected another event."),
543 }
544 }
545
546 fn create_viewport(min: f32, max: f32) -> pointerinjector::Viewport {
548 pointerinjector::Viewport {
549 extents: Some([[min, min], [max, max]]),
550 viewport_to_context_transform: None,
551 ..Default::default()
552 }
553 }
554
555 #[fuchsia::test]
558 fn receives_viewport_updates() {
559 let mut exec = fasync::TestExecutor::new();
560
561 let (configuration_proxy, mut configuration_request_stream) =
563 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
564 let (injector_registry_proxy, _injector_registry_request_stream) =
565 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
566 let inspector = fuchsia_inspect::Inspector::default();
567 let test_node = inspector.root().create_child("test_node");
568 let touch_handler_fut = TouchInjectorHandler::new_handler(
569 configuration_proxy,
570 injector_registry_proxy,
571 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
572 &test_node,
573 metrics::MetricsLogger::default(),
574 );
575 let config_request_stream_fut =
576 handle_configuration_request_stream(&mut configuration_request_stream);
577 let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
578 touch_handler_fut,
579 config_request_stream_fut,
580 ));
581 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
582
583 let (injector_device_proxy, mut injector_device_request_stream) =
585 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
586 touch_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
587
588 {
590 let watch_viewport_fut = touch_handler.clone().watch_viewport();
592 futures::pin_mut!(watch_viewport_fut);
593 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
594
595 match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
597 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
598 responder, ..
599 })) => {
600 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
601 }
602 other => panic!("Received unexpected value: {:?}", other),
603 };
604 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
605
606 exec.run_singlethreaded(async {
608 match injector_device_request_stream.next().await {
609 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
610 assert_eq!(events.len(), 1);
611 assert!(events[0].data.is_some());
612 assert_eq!(
613 events[0].data,
614 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
615 );
616 responder.send().expect("injector stream failed to respond.");
617 }
618 other => panic!("Received unexpected value: {:?}", other),
619 }
620 });
621
622 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
624
625 match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
627 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport {
628 responder, ..
629 })) => {
630 responder
631 .send(&create_viewport(100.0, 200.0))
632 .expect("Failed to send viewport.");
633 }
634 other => panic!("Received unexpected value: {:?}", other),
635 };
636
637 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
639
640 exec.run_singlethreaded(async {
642 match injector_device_request_stream.next().await {
643 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
644 assert_eq!(events.len(), 1);
645 assert!(events[0].data.is_some());
646 assert_eq!(
647 events[0].data,
648 Some(pointerinjector::Data::Viewport(create_viewport(100.0, 200.0)))
649 );
650 responder.send().expect("injector stream failed to respond.");
651 }
652 other => panic!("Received unexpected value: {:?}", other),
653 }
654 });
655 }
656
657 let expected_viewport = create_viewport(100.0, 200.0);
659 assert_eq!(touch_handler.mutable_state.borrow().viewport, Some(expected_viewport));
660 }
661
662 #[fuchsia::test]
664 fn add_contact_drops_without_viewport() {
665 let mut exec = fasync::TestExecutor::new();
666
667 let (configuration_proxy, mut configuration_request_stream) =
669 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
670 let (injector_registry_proxy, mut injector_registry_request_stream) =
671 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
672 let config_request_stream_fut =
673 handle_configuration_request_stream(&mut configuration_request_stream);
674
675 let inspector = fuchsia_inspect::Inspector::default();
676 let test_node = inspector.root().create_child("test_node");
677
678 let touch_handler_fut = TouchInjectorHandler::new_handler(
680 configuration_proxy,
681 injector_registry_proxy,
682 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
683 &test_node,
684 metrics::MetricsLogger::default(),
685 );
686 let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
687 touch_handler_fut,
688 config_request_stream_fut,
689 ));
690 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
691
692 let event_time = zx::MonotonicInstant::get();
694 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
695 let descriptor = get_touch_screen_device_descriptor();
696 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
697 hashmap! {
698 fidl_ui_input::PointerEventPhase::Add
699 => vec![contact.clone()],
700 },
701 event_time,
702 &descriptor,
703 ))
704 .unwrap();
705
706 let mut handle_event_fut = touch_handler.clone().handle_unhandled_input_event(input_event);
711 let _ = exec.run_until_stalled(&mut handle_event_fut);
712
713 let mut ir_fut = injector_registry_request_stream.next();
715 assert_matches!(exec.run_until_stalled(&mut ir_fut), futures::task::Poll::Pending);
716 }
717
718 #[fuchsia::test]
720 fn add_contact_succeeds_with_viewport() {
721 let mut exec = fasync::TestExecutor::new();
722
723 let (configuration_proxy, mut configuration_request_stream) =
725 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
726 let (injector_registry_proxy, _injector_registry_request_stream) =
727 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
728 let inspector = fuchsia_inspect::Inspector::default();
729 let test_node = inspector.root().create_child("test_node");
730 let touch_handler_fut = TouchInjectorHandler::new_handler(
731 configuration_proxy,
732 injector_registry_proxy,
733 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
734 &test_node,
735 metrics::MetricsLogger::default(),
736 );
737 let config_request_stream_fut =
738 handle_configuration_request_stream(&mut configuration_request_stream);
739 let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
740 touch_handler_fut,
741 config_request_stream_fut,
742 ));
743 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
744
745 let (injector_device_proxy, mut injector_device_request_stream) =
747 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
748 touch_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
749
750 let watch_viewport_fut = fasync::Task::local(touch_handler.clone().watch_viewport());
752 futures::pin_mut!(watch_viewport_fut);
753 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
754
755 match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
757 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
758 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
759 }
760 other => panic!("Received unexpected value: {:?}", other),
761 };
762 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
763
764 exec.run_singlethreaded(async {
766 match injector_device_request_stream.next().await {
767 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
768 assert_eq!(events.len(), 1);
769 assert!(events[0].data.is_some());
770 assert_eq!(
771 events[0].data,
772 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
773 );
774 responder.send().expect("injector stream failed to respond.");
775 }
776 other => panic!("Received unexpected value: {:?}", other),
777 }
778 });
779
780 let event_time = zx::MonotonicInstant::get();
782 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
783 let descriptor = get_touch_screen_device_descriptor();
784 let input_event = input_device::UnhandledInputEvent::try_from(create_touch_screen_event(
785 hashmap! {
786 fidl_ui_input::PointerEventPhase::Add
787 => vec![contact.clone()],
788 },
789 event_time,
790 &descriptor,
791 ))
792 .unwrap();
793
794 let handle_event_fut = touch_handler.clone().handle_unhandled_input_event(input_event);
796
797 let expected_event = create_touch_pointer_sample_event(
799 pointerinjector::EventPhase::Add,
800 &contact,
801 Position { x: 20.0, y: 40.0 },
802 event_time,
803 );
804
805 let device_fut =
808 handle_device_request_stream(injector_device_request_stream, expected_event);
809 let (handle_result, _) =
810 exec.run_singlethreaded(futures::future::join(handle_event_fut, device_fut));
811
812 assert_matches!(
814 handle_result.as_slice(),
815 [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
816 );
817 }
818
819 #[fuchsia::test]
821 fn add_touchpad_contact_with_viewport() {
822 let mut exec = fasync::TestExecutor::new();
823
824 let (configuration_proxy, mut configuration_request_stream) =
826 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
827 let (injector_registry_proxy, mut injector_registry_request_stream) =
828 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
829 let inspector = fuchsia_inspect::Inspector::default();
830 let test_node = inspector.root().create_child("test_node");
831 let touch_handler_fut = TouchInjectorHandler::new_handler(
832 configuration_proxy,
833 injector_registry_proxy,
834 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
835 &test_node,
836 metrics::MetricsLogger::default(),
837 );
838 let config_request_stream_fut =
839 handle_configuration_request_stream(&mut configuration_request_stream);
840 let (touch_handler_res, _) = exec.run_singlethreaded(futures::future::join(
841 touch_handler_fut,
842 config_request_stream_fut,
843 ));
844 let touch_handler = touch_handler_res.expect("Failed to create touch handler.");
845
846 let (injector_device_proxy, mut injector_device_request_stream) =
848 fidl::endpoints::create_proxy_and_stream::<pointerinjector::DeviceMarker>();
849 touch_handler.mutable_state.borrow_mut().injectors.insert(1, injector_device_proxy);
850
851 let watch_viewport_fut = fasync::Task::local(touch_handler.clone().watch_viewport());
853 futures::pin_mut!(watch_viewport_fut);
854 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
855
856 match exec.run_singlethreaded(&mut configuration_request_stream.next()) {
858 Some(Ok(pointerinjector_config::SetupRequest::WatchViewport { responder, .. })) => {
859 responder.send(&create_viewport(0.0, 100.0)).expect("Failed to send viewport.");
860 }
861 other => panic!("Received unexpected value: {:?}", other),
862 };
863 assert!(exec.run_until_stalled(&mut watch_viewport_fut).is_pending());
864
865 exec.run_singlethreaded(async {
867 match injector_device_request_stream.next().await {
868 Some(Ok(pointerinjector::DeviceRequest::Inject { events, responder })) => {
869 assert_eq!(events.len(), 1);
870 assert!(events[0].data.is_some());
871 assert_eq!(
872 events[0].data,
873 Some(pointerinjector::Data::Viewport(create_viewport(0.0, 100.0)))
874 );
875 responder.send().expect("injector stream failed to respond.");
876 }
877 other => panic!("Received unexpected value: {:?}", other),
878 }
879 });
880
881 let event_time = zx::MonotonicInstant::get();
883 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
884 let descriptor = get_touchpad_device_descriptor();
885 let input_event = input_device::UnhandledInputEvent::try_from(create_touchpad_event(
886 vec![contact.clone()],
887 HashSet::new(),
888 event_time,
889 &descriptor,
890 ))
891 .unwrap();
892
893 let handle_event_fut = touch_handler.clone().handle_unhandled_input_event(input_event);
895
896 let handle_result = exec.run_singlethreaded(handle_event_fut);
897
898 assert_matches!(
900 handle_result.as_slice(),
901 [input_device::InputEvent { handled: input_device::Handled::No, .. }]
902 );
903
904 let mut ir_fut = injector_registry_request_stream.next();
906 assert_matches!(exec.run_until_stalled(&mut ir_fut), futures::task::Poll::Pending);
907 }
908
909 #[fuchsia::test(allow_stalls = false)]
910 async fn touch_injector_handler_initialized_with_inspect_node() {
911 let (configuration_proxy, mut configuration_request_stream) =
912 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
913 let inspector = fuchsia_inspect::Inspector::default();
914 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
915 let touch_handler_fut = TouchInjectorHandler::new_with_config_proxy(
916 configuration_proxy,
917 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
918 &fake_handlers_node,
919 metrics::MetricsLogger::default(),
920 );
921 let config_request_stream_fut =
922 handle_configuration_request_stream(&mut configuration_request_stream);
923 let (touch_handler_res, _) = futures::join!(touch_handler_fut, config_request_stream_fut,);
924 let _handler = touch_handler_res.expect("Failed to create touch handler.");
925 diagnostics_assertions::assert_data_tree!(inspector, root: {
926 input_handlers_node: {
927 touch_injector_handler: {
928 events_received_count: 0u64,
929 events_handled_count: 0u64,
930 last_received_timestamp_ns: 0u64,
931 "fuchsia.inspect.Health": {
932 status: "STARTING_UP",
933 start_timestamp_nanos: diagnostics_assertions::AnyProperty
936 },
937 }
938 }
939 });
940 }
941
942 #[fuchsia::test(allow_stalls = false)]
943 async fn touch_injector_handler_inspect_counts_events() {
944 let (configuration_proxy, mut configuration_request_stream) =
946 fidl::endpoints::create_proxy_and_stream::<pointerinjector_config::SetupMarker>();
947 let (injector_registry_proxy, _) =
948 fidl::endpoints::create_proxy_and_stream::<pointerinjector::RegistryMarker>();
949
950 let inspector = fuchsia_inspect::Inspector::default();
951 let fake_handlers_node = inspector.root().create_child("input_handlers_node");
952
953 let touch_handler_fut = TouchInjectorHandler::new_handler(
955 configuration_proxy,
956 injector_registry_proxy,
957 Size { width: DISPLAY_WIDTH, height: DISPLAY_HEIGHT },
958 &fake_handlers_node,
959 metrics::MetricsLogger::default(),
960 );
961 let config_request_stream_fut =
962 handle_configuration_request_stream(&mut configuration_request_stream);
963 let (touch_handler_res, _) = futures::join!(touch_handler_fut, config_request_stream_fut);
964 let touch_handler = touch_handler_res.expect("Failed to create touch handler");
965
966 let contact = create_touch_contact(TOUCH_ID, Position { x: 20.0, y: 40.0 });
967 let descriptor = get_touch_screen_device_descriptor();
968 let event_time1 = zx::MonotonicInstant::get();
969 let event_time2 = event_time1.add(zx::MonotonicDuration::from_micros(1));
970 let event_time3 = event_time2.add(zx::MonotonicDuration::from_micros(1));
971
972 let input_events = vec![
973 create_touch_screen_event(
974 hashmap! {
975 fidl_ui_input::PointerEventPhase::Add
976 => vec![contact.clone()],
977 },
978 event_time1,
979 &descriptor,
980 ),
981 create_touch_screen_event(
982 hashmap! {
983 fidl_ui_input::PointerEventPhase::Move
984 => vec![contact.clone()],
985 },
986 event_time2,
987 &descriptor,
988 ),
989 create_fake_input_event(event_time2),
991 create_touch_screen_event_with_handled(
993 hashmap! {
994 fidl_ui_input::PointerEventPhase::Move
995 => vec![contact.clone()],
996 },
997 event_time2,
998 &descriptor,
999 input_device::Handled::Yes,
1000 ),
1001 create_touch_screen_event(
1002 hashmap! {
1003 fidl_ui_input::PointerEventPhase::Remove
1004 => vec![contact.clone()],
1005 },
1006 event_time3,
1007 &descriptor,
1008 ),
1009 ];
1010
1011 for input_event in input_events {
1012 touch_handler.clone().handle_input_event(input_event).await;
1013 }
1014
1015 let last_received_event_time: u64 = event_time3.into_nanos().try_into().unwrap();
1016
1017 diagnostics_assertions::assert_data_tree!(inspector, root: {
1018 input_handlers_node: {
1019 touch_injector_handler: {
1020 events_received_count: 3u64,
1021 events_handled_count: 3u64,
1022 last_received_timestamp_ns: last_received_event_time,
1023 "fuchsia.inspect.Health": {
1024 status: "STARTING_UP",
1025 start_timestamp_nanos: diagnostics_assertions::AnyProperty
1028 },
1029 }
1030 }
1031 });
1032 }
1033}