1use crate::{
6 consumer_controls_binding, keyboard_binding, light_sensor_binding, metrics, mouse_binding,
7 touch_binding,
8};
9use anyhow::{format_err, Error};
10use async_trait::async_trait;
11use async_utils::hanging_get::client::HangingGetStream;
12use fidl::endpoints::Proxy;
13use fidl_fuchsia_input_report::{InputDeviceMarker, InputReport};
14use fuchsia_inspect::health::Reporter;
15use fuchsia_inspect::{
16 ExponentialHistogramParams, HistogramProperty as _, NumericProperty, Property,
17};
18use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
19use futures::stream::StreamExt;
20use metrics_registry::*;
21use std::path::PathBuf;
22use {
23 fidl_fuchsia_input_report as fidl_input_report, fidl_fuchsia_io as fio,
24 fuchsia_async as fasync, fuchsia_trace as ftrace,
25};
26
27pub use input_device_constants::InputDeviceType;
28
29pub static INPUT_REPORT_PATH: &str = "/dev/class/input-report";
31
32const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
33 floor: 0,
34 initial_step: 1,
35 step_multiplier: 10,
36 buckets: 7,
47};
48
49pub struct InputDeviceStatus {
52 now: Box<dyn Fn() -> zx::MonotonicInstant>,
55
56 _node: fuchsia_inspect::Node,
58
59 reports_received_count: fuchsia_inspect::UintProperty,
61
62 reports_filtered_count: fuchsia_inspect::UintProperty,
65
66 events_generated: fuchsia_inspect::UintProperty,
69
70 last_received_timestamp_ns: fuchsia_inspect::UintProperty,
72
73 last_generated_timestamp_ns: fuchsia_inspect::UintProperty,
75
76 pub health_node: fuchsia_inspect::health::Node,
78
79 driver_to_binding_latency_ms: fuchsia_inspect::IntExponentialHistogramProperty,
84}
85
86impl InputDeviceStatus {
87 pub fn new(device_node: fuchsia_inspect::Node) -> Self {
88 Self::new_internal(device_node, Box::new(zx::MonotonicInstant::get))
89 }
90
91 fn new_internal(
92 device_node: fuchsia_inspect::Node,
93 now: Box<dyn Fn() -> zx::MonotonicInstant>,
94 ) -> Self {
95 let mut health_node = fuchsia_inspect::health::Node::new(&device_node);
96 health_node.set_starting_up();
97
98 let reports_received_count = device_node.create_uint("reports_received_count", 0);
99 let reports_filtered_count = device_node.create_uint("reports_filtered_count", 0);
100 let events_generated = device_node.create_uint("events_generated", 0);
101 let last_received_timestamp_ns = device_node.create_uint("last_received_timestamp_ns", 0);
102 let last_generated_timestamp_ns = device_node.create_uint("last_generated_timestamp_ns", 0);
103 let driver_to_binding_latency_ms = device_node.create_int_exponential_histogram(
104 "driver_to_binding_latency_ms",
105 LATENCY_HISTOGRAM_PROPERTIES,
106 );
107
108 Self {
109 now,
110 _node: device_node,
111 reports_received_count,
112 reports_filtered_count,
113 events_generated,
114 last_received_timestamp_ns,
115 last_generated_timestamp_ns,
116 health_node,
117 driver_to_binding_latency_ms,
118 }
119 }
120
121 pub fn count_received_report(&self, report: &InputReport) {
122 self.reports_received_count.add(1);
123 match report.event_time {
124 Some(event_time) => {
125 self.driver_to_binding_latency_ms.insert(
126 ((self.now)() - zx::MonotonicInstant::from_nanos(event_time)).into_millis(),
127 );
128 self.last_received_timestamp_ns.set(event_time.try_into().unwrap());
129 }
130 None => (),
131 }
132 }
133
134 pub fn count_filtered_report(&self) {
135 self.reports_filtered_count.add(1);
136 }
137
138 pub fn count_generated_event(&self, event: InputEvent) {
139 self.events_generated.add(1);
140 self.last_generated_timestamp_ns.set(event.event_time.into_nanos().try_into().unwrap());
141 }
142}
143
144#[derive(Clone, Debug, PartialEq)]
146pub struct InputEvent {
147 pub device_event: InputDeviceEvent,
149
150 pub device_descriptor: InputDeviceDescriptor,
153
154 pub event_time: zx::MonotonicInstant,
156
157 pub handled: Handled,
159
160 pub trace_id: Option<ftrace::Id>,
161}
162
163#[derive(Clone, Debug, PartialEq)]
169pub struct UnhandledInputEvent {
170 pub device_event: InputDeviceEvent,
172
173 pub device_descriptor: InputDeviceDescriptor,
176
177 pub event_time: zx::MonotonicInstant,
179
180 pub trace_id: Option<ftrace::Id>,
181}
182
183#[derive(Clone, Debug, PartialEq)]
192pub enum InputDeviceEvent {
193 Keyboard(keyboard_binding::KeyboardEvent),
194 LightSensor(light_sensor_binding::LightSensorEvent),
195 ConsumerControls(consumer_controls_binding::ConsumerControlsEvent),
196 Mouse(mouse_binding::MouseEvent),
197 TouchScreen(touch_binding::TouchScreenEvent),
198 Touchpad(touch_binding::TouchpadEvent),
199 #[cfg(test)]
200 Fake,
201}
202
203#[derive(Clone, Debug, PartialEq)]
213pub enum InputDeviceDescriptor {
214 Keyboard(keyboard_binding::KeyboardDeviceDescriptor),
215 LightSensor(light_sensor_binding::LightSensorDeviceDescriptor),
216 ConsumerControls(consumer_controls_binding::ConsumerControlsDeviceDescriptor),
217 Mouse(mouse_binding::MouseDeviceDescriptor),
218 TouchScreen(touch_binding::TouchScreenDeviceDescriptor),
219 Touchpad(touch_binding::TouchpadDeviceDescriptor),
220 #[cfg(test)]
221 Fake,
222}
223
224impl From<keyboard_binding::KeyboardDeviceDescriptor> for InputDeviceDescriptor {
225 fn from(b: keyboard_binding::KeyboardDeviceDescriptor) -> Self {
226 InputDeviceDescriptor::Keyboard(b)
227 }
228}
229
230#[derive(Copy, Clone, Debug, PartialEq)]
232pub enum Handled {
233 Yes,
235 No,
237}
238
239#[async_trait]
248pub trait InputDeviceBinding: Send {
249 fn get_device_descriptor(&self) -> InputDeviceDescriptor;
251
252 fn input_event_sender(&self) -> UnboundedSender<InputEvent>;
254}
255
256pub fn initialize_report_stream<InputDeviceProcessReportsFn>(
271 device_proxy: fidl_input_report::InputDeviceProxy,
272 device_descriptor: InputDeviceDescriptor,
273 mut event_sender: UnboundedSender<InputEvent>,
274 inspect_status: InputDeviceStatus,
275 metrics_logger: metrics::MetricsLogger,
276 mut process_reports: InputDeviceProcessReportsFn,
277) where
278 InputDeviceProcessReportsFn: 'static
279 + Send
280 + FnMut(
281 InputReport,
282 Option<InputReport>,
283 &InputDeviceDescriptor,
284 &mut UnboundedSender<InputEvent>,
285 &InputDeviceStatus,
286 &metrics::MetricsLogger,
287 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>),
288{
289 fasync::Task::local(async move {
290 let mut previous_report: Option<InputReport> = None;
291 let (report_reader, server_end) = fidl::endpoints::create_proxy();
292 let result = device_proxy.get_input_reports_reader(server_end);
293 if result.is_err() {
294 metrics_logger.log_error(
295 InputPipelineErrorMetricDimensionEvent::InputDeviceGetInputReportsReaderError,
296 std::format!("error on GetInputReportsReader: {:?}", &result),
297 );
298 return; }
300 let mut report_stream = HangingGetStream::new(
301 report_reader,
302 fidl_input_report::InputReportsReaderProxy::read_input_reports,
303 );
304 loop {
305 match report_stream.next().await {
306 Some(Ok(Ok(input_reports))) => {
307 fuchsia_trace::duration!(c"input", c"input-device-process-reports");
308 let mut inspect_receiver: Option<UnboundedReceiver<InputEvent>>;
309 for report in input_reports {
310 (previous_report, inspect_receiver) = process_reports(
311 report,
312 previous_report,
313 &device_descriptor,
314 &mut event_sender,
315 &inspect_status,
316 &metrics_logger,
317 );
318 match inspect_receiver {
322 Some(mut receiver) => {
323 while let Some(event) = receiver.next().await {
324 inspect_status.count_generated_event(event);
325 }
326 }
327 None => (),
328 };
329 }
330 }
331 Some(Ok(Err(_service_error))) => break,
332 Some(Err(_fidl_error)) => break,
333 None => break,
334 }
335 }
336 log::warn!("initialize_report_stream exited - device binding no longer works");
339 })
340 .detach();
341}
342
343pub async fn is_device_type(
349 device_descriptor: &fidl_input_report::DeviceDescriptor,
350 device_type: InputDeviceType,
351) -> bool {
352 match device_type {
354 InputDeviceType::ConsumerControls => device_descriptor.consumer_control.is_some(),
355 InputDeviceType::Mouse => device_descriptor.mouse.is_some(),
356 InputDeviceType::Touch => device_descriptor.touch.is_some(),
357 InputDeviceType::Keyboard => device_descriptor.keyboard.is_some(),
358 InputDeviceType::LightSensor => device_descriptor.sensor.is_some(),
359 }
360}
361
362pub async fn get_device_binding(
370 device_type: InputDeviceType,
371 device_proxy: fidl_input_report::InputDeviceProxy,
372 device_id: u32,
373 input_event_sender: UnboundedSender<InputEvent>,
374 device_node: fuchsia_inspect::Node,
375 metrics_logger: metrics::MetricsLogger,
376) -> Result<Box<dyn InputDeviceBinding>, Error> {
377 match device_type {
378 InputDeviceType::ConsumerControls => {
379 let binding = consumer_controls_binding::ConsumerControlsBinding::new(
380 device_proxy,
381 device_id,
382 input_event_sender,
383 device_node,
384 metrics_logger,
385 )
386 .await?;
387 Ok(Box::new(binding))
388 }
389 InputDeviceType::Mouse => {
390 let binding = mouse_binding::MouseBinding::new(
391 device_proxy,
392 device_id,
393 input_event_sender,
394 device_node,
395 metrics_logger,
396 )
397 .await?;
398 Ok(Box::new(binding))
399 }
400 InputDeviceType::Touch => {
401 let binding = touch_binding::TouchBinding::new(
402 device_proxy,
403 device_id,
404 input_event_sender,
405 device_node,
406 metrics_logger,
407 )
408 .await?;
409 Ok(Box::new(binding))
410 }
411 InputDeviceType::Keyboard => {
412 let binding = keyboard_binding::KeyboardBinding::new(
413 device_proxy,
414 device_id,
415 input_event_sender,
416 device_node,
417 metrics_logger,
418 )
419 .await?;
420 Ok(Box::new(binding))
421 }
422 InputDeviceType::LightSensor => {
423 let binding = light_sensor_binding::LightSensorBinding::new(
424 device_proxy,
425 device_id,
426 input_event_sender,
427 device_node,
428 metrics_logger,
429 )
430 .await?;
431 Ok(Box::new(binding))
432 }
433 }
434}
435
436pub fn get_device_from_dir_entry_path(
445 dir_proxy: &fio::DirectoryProxy,
446 entry_path: &PathBuf,
447) -> Result<fidl_input_report::InputDeviceProxy, Error> {
448 let input_device_path = entry_path.to_str();
449 if input_device_path.is_none() {
450 return Err(format_err!("Failed to get entry path as a string."));
451 }
452
453 let (input_device, server) = fidl::endpoints::create_proxy::<InputDeviceMarker>();
454 fdio::service_connect_at(
455 dir_proxy.as_channel().as_ref(),
456 input_device_path.unwrap(),
457 server.into_channel(),
458 )
459 .expect("Failed to connect to InputDevice.");
460 Ok(input_device)
461}
462
463pub fn event_time_or_now(event_time: Option<i64>) -> zx::MonotonicInstant {
468 match event_time {
469 Some(time) => zx::MonotonicInstant::from_nanos(time),
470 None => zx::MonotonicInstant::get(),
471 }
472}
473
474impl std::convert::From<UnhandledInputEvent> for InputEvent {
475 fn from(event: UnhandledInputEvent) -> Self {
476 Self {
477 device_event: event.device_event,
478 device_descriptor: event.device_descriptor,
479 event_time: event.event_time,
480 handled: Handled::No,
481 trace_id: event.trace_id,
482 }
483 }
484}
485
486#[cfg(test)]
493impl std::convert::TryFrom<InputEvent> for UnhandledInputEvent {
494 type Error = anyhow::Error;
495 fn try_from(event: InputEvent) -> Result<UnhandledInputEvent, Self::Error> {
496 match event.handled {
497 Handled::Yes => {
498 Err(format_err!("Attempted to treat a handled InputEvent as unhandled"))
499 }
500 Handled::No => Ok(UnhandledInputEvent {
501 device_event: event.device_event,
502 device_descriptor: event.device_descriptor,
503 event_time: event.event_time,
504 trace_id: event.trace_id,
505 }),
506 }
507 }
508}
509
510impl InputEvent {
511 pub(crate) fn into_handled_if(self, predicate: bool) -> Self {
514 if predicate {
515 Self { handled: Handled::Yes, ..self }
516 } else {
517 self
518 }
519 }
520
521 pub(crate) fn into_handled(self) -> Self {
523 Self { handled: Handled::Yes, ..self }
524 }
525
526 pub fn into_with_event_time(self, event_time: zx::MonotonicInstant) -> Self {
528 Self { event_time, ..self }
529 }
530
531 #[cfg(test)]
533 pub fn into_with_device_descriptor(self, device_descriptor: InputDeviceDescriptor) -> Self {
534 Self { device_descriptor, ..self }
535 }
536
537 pub fn is_handled(&self) -> bool {
539 self.handled == Handled::Yes
540 }
541
542 pub fn get_event_type(&self) -> &'static str {
544 match self.device_event {
545 InputDeviceEvent::Keyboard(_) => "keyboard_event",
546 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
547 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
548 InputDeviceEvent::Mouse(_) => "mouse_event",
549 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
550 InputDeviceEvent::Touchpad(_) => "touchpad_event",
551 #[cfg(test)]
552 InputDeviceEvent::Fake => "fake_event",
553 }
554 }
555
556 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
557 node.record_int("event_time", self.event_time.into_nanos());
558 match &self.device_event {
559 InputDeviceEvent::LightSensor(e) => e.record_inspect(node),
560 InputDeviceEvent::ConsumerControls(e) => e.record_inspect(node),
561 InputDeviceEvent::Mouse(e) => e.record_inspect(node),
562 InputDeviceEvent::TouchScreen(e) => e.record_inspect(node),
563 InputDeviceEvent::Touchpad(e) => e.record_inspect(node),
564 InputDeviceEvent::Keyboard(_) => (),
566 #[cfg(test)] InputDeviceEvent::Fake => (),
568 }
569 }
570}
571
572#[cfg(test)]
573mod tests {
574 use super::*;
575 use assert_matches::assert_matches;
576 use diagnostics_assertions::AnyProperty;
577 use fidl_test_util::spawn_stream_handler;
578
579 use pretty_assertions::assert_eq;
580 use std::convert::TryFrom as _;
581 use test_case::test_case;
582
583 #[test]
584 fn max_event_time() {
585 let event_time = event_time_or_now(Some(i64::MAX));
586 assert_eq!(event_time, zx::MonotonicInstant::INFINITE);
587 }
588
589 #[test]
590 fn min_event_time() {
591 let event_time = event_time_or_now(Some(std::i64::MIN));
592 assert_eq!(event_time, zx::MonotonicInstant::INFINITE_PAST);
593 }
594
595 #[fasync::run_singlethreaded(test)]
596 async fn input_device_status_initialized_with_correct_properties() {
597 let inspector = fuchsia_inspect::Inspector::default();
598 let input_pipeline_node = inspector.root().create_child("input_pipeline");
599 let input_devices_node = input_pipeline_node.create_child("input_devices");
600 let device_node = input_devices_node.create_child("001_keyboard");
601 let _input_device_status = InputDeviceStatus::new(device_node);
602 diagnostics_assertions::assert_data_tree!(inspector, root: {
603 input_pipeline: {
604 input_devices: {
605 "001_keyboard": {
606 reports_received_count: 0u64,
607 reports_filtered_count: 0u64,
608 events_generated: 0u64,
609 last_received_timestamp_ns: 0u64,
610 last_generated_timestamp_ns: 0u64,
611 "fuchsia.inspect.Health": {
612 status: "STARTING_UP",
613 start_timestamp_nanos: AnyProperty
616 },
617 driver_to_binding_latency_ms: diagnostics_assertions::HistogramAssertion::exponential(super::LATENCY_HISTOGRAM_PROPERTIES),
618 }
619 }
620 }
621 });
622 }
623
624 #[test_case(i64::MIN; "min value")]
625 #[test_case(-1; "negative value")]
626 #[test_case(0; "zero")]
627 #[test_case(1; "positive value")]
628 #[test_case(i64::MAX; "max value")]
629 #[fuchsia::test(allow_stalls = false)]
630 async fn input_device_status_updates_latency_histogram_on_count_received_report(
631 latency_nsec: i64,
632 ) {
633 let mut expected_histogram = diagnostics_assertions::HistogramAssertion::exponential(
634 super::LATENCY_HISTOGRAM_PROPERTIES,
635 );
636 let inspector = fuchsia_inspect::Inspector::default();
637 let input_device_status = InputDeviceStatus::new_internal(
638 inspector.root().clone_weak(),
639 Box::new(move || zx::MonotonicInstant::from_nanos(latency_nsec)),
640 );
641 input_device_status
642 .count_received_report(&InputReport { event_time: Some(0), ..InputReport::default() });
643 expected_histogram.insert_values([latency_nsec / 1000 / 1000]);
644 diagnostics_assertions::assert_data_tree!(inspector, root: contains {
645 driver_to_binding_latency_ms: expected_histogram,
646 });
647 }
648
649 #[fasync::run_singlethreaded(test)]
652 async fn consumer_controls_input_device_exists() {
653 let input_device_proxy: fidl_input_report::InputDeviceProxy =
654 spawn_stream_handler(move |input_device_request| async move {
655 match input_device_request {
656 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
657 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
658 device_information: None,
659 mouse: None,
660 sensor: None,
661 touch: None,
662 keyboard: None,
663 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
664 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
665 buttons: Some(vec![
666 fidl_input_report::ConsumerControlButton::VolumeUp,
667 fidl_input_report::ConsumerControlButton::VolumeDown,
668 ]),
669 ..Default::default()
670 }),
671 ..Default::default()
672 }),
673 ..Default::default()
674 });
675 }
676 _ => panic!("InputDevice handler received an unexpected request"),
677 }
678 });
679
680 assert!(
681 is_device_type(
682 &input_device_proxy
683 .get_descriptor()
684 .await
685 .expect("Failed to get device descriptor"),
686 InputDeviceType::ConsumerControls
687 )
688 .await
689 );
690 }
691
692 #[fasync::run_singlethreaded(test)]
694 async fn mouse_input_device_exists() {
695 let input_device_proxy: fidl_input_report::InputDeviceProxy =
696 spawn_stream_handler(move |input_device_request| async move {
697 match input_device_request {
698 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
699 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
700 device_information: None,
701 mouse: Some(fidl_input_report::MouseDescriptor {
702 input: Some(fidl_input_report::MouseInputDescriptor {
703 movement_x: None,
704 movement_y: None,
705 position_x: None,
706 position_y: None,
707 scroll_v: None,
708 scroll_h: None,
709 buttons: None,
710 ..Default::default()
711 }),
712 ..Default::default()
713 }),
714 sensor: None,
715 touch: None,
716 keyboard: None,
717 consumer_control: None,
718 ..Default::default()
719 });
720 }
721 _ => panic!("InputDevice handler received an unexpected request"),
722 }
723 });
724
725 assert!(
726 is_device_type(
727 &input_device_proxy
728 .get_descriptor()
729 .await
730 .expect("Failed to get device descriptor"),
731 InputDeviceType::Mouse
732 )
733 .await
734 );
735 }
736
737 #[fasync::run_singlethreaded(test)]
740 async fn mouse_input_device_doesnt_exist() {
741 let input_device_proxy: fidl_input_report::InputDeviceProxy =
742 spawn_stream_handler(move |input_device_request| async move {
743 match input_device_request {
744 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
745 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
746 device_information: None,
747 mouse: None,
748 sensor: None,
749 touch: None,
750 keyboard: None,
751 consumer_control: None,
752 ..Default::default()
753 });
754 }
755 _ => panic!("InputDevice handler received an unexpected request"),
756 }
757 });
758
759 assert!(
760 !is_device_type(
761 &input_device_proxy
762 .get_descriptor()
763 .await
764 .expect("Failed to get device descriptor"),
765 InputDeviceType::Mouse
766 )
767 .await
768 );
769 }
770
771 #[fasync::run_singlethreaded(test)]
774 async fn touch_input_device_exists() {
775 let input_device_proxy: fidl_input_report::InputDeviceProxy =
776 spawn_stream_handler(move |input_device_request| async move {
777 match input_device_request {
778 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
779 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
780 device_information: None,
781 mouse: None,
782 sensor: None,
783 touch: Some(fidl_input_report::TouchDescriptor {
784 input: Some(fidl_input_report::TouchInputDescriptor {
785 contacts: None,
786 max_contacts: None,
787 touch_type: None,
788 buttons: None,
789 ..Default::default()
790 }),
791 ..Default::default()
792 }),
793 keyboard: None,
794 consumer_control: None,
795 ..Default::default()
796 });
797 }
798 _ => panic!("InputDevice handler received an unexpected request"),
799 }
800 });
801
802 assert!(
803 is_device_type(
804 &input_device_proxy
805 .get_descriptor()
806 .await
807 .expect("Failed to get device descriptor"),
808 InputDeviceType::Touch
809 )
810 .await
811 );
812 }
813
814 #[fasync::run_singlethreaded(test)]
817 async fn touch_input_device_doesnt_exist() {
818 let input_device_proxy: fidl_input_report::InputDeviceProxy =
819 spawn_stream_handler(move |input_device_request| async move {
820 match input_device_request {
821 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
822 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
823 device_information: None,
824 mouse: None,
825 sensor: None,
826 touch: None,
827 keyboard: None,
828 consumer_control: None,
829 ..Default::default()
830 });
831 }
832 _ => panic!("InputDevice handler received an unexpected request"),
833 }
834 });
835
836 assert!(
837 !is_device_type(
838 &input_device_proxy
839 .get_descriptor()
840 .await
841 .expect("Failed to get device descriptor"),
842 InputDeviceType::Touch
843 )
844 .await
845 );
846 }
847
848 #[fasync::run_singlethreaded(test)]
851 async fn keyboard_input_device_exists() {
852 let input_device_proxy: fidl_input_report::InputDeviceProxy =
853 spawn_stream_handler(move |input_device_request| async move {
854 match input_device_request {
855 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
856 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
857 device_information: None,
858 mouse: None,
859 sensor: None,
860 touch: None,
861 keyboard: Some(fidl_input_report::KeyboardDescriptor {
862 input: Some(fidl_input_report::KeyboardInputDescriptor {
863 keys3: None,
864 ..Default::default()
865 }),
866 output: None,
867 ..Default::default()
868 }),
869 consumer_control: None,
870 ..Default::default()
871 });
872 }
873 _ => panic!("InputDevice handler received an unexpected request"),
874 }
875 });
876
877 assert!(
878 is_device_type(
879 &input_device_proxy
880 .get_descriptor()
881 .await
882 .expect("Failed to get device descriptor"),
883 InputDeviceType::Keyboard
884 )
885 .await
886 );
887 }
888
889 #[fasync::run_singlethreaded(test)]
892 async fn keyboard_input_device_doesnt_exist() {
893 let input_device_proxy: fidl_input_report::InputDeviceProxy =
894 spawn_stream_handler(move |input_device_request| async move {
895 match input_device_request {
896 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
897 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
898 device_information: None,
899 mouse: None,
900 sensor: None,
901 touch: None,
902 keyboard: None,
903 consumer_control: None,
904 ..Default::default()
905 });
906 }
907 _ => panic!("InputDevice handler received an unexpected request"),
908 }
909 });
910
911 assert!(
912 !is_device_type(
913 &input_device_proxy
914 .get_descriptor()
915 .await
916 .expect("Failed to get device descriptor"),
917 InputDeviceType::Keyboard
918 )
919 .await
920 );
921 }
922
923 #[fasync::run_singlethreaded(test)]
925 async fn no_input_device_match() {
926 let input_device_proxy: fidl_input_report::InputDeviceProxy =
927 spawn_stream_handler(move |input_device_request| async move {
928 match input_device_request {
929 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
930 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
931 device_information: None,
932 mouse: Some(fidl_input_report::MouseDescriptor {
933 input: Some(fidl_input_report::MouseInputDescriptor {
934 movement_x: None,
935 movement_y: None,
936 position_x: None,
937 position_y: None,
938 scroll_v: None,
939 scroll_h: None,
940 buttons: None,
941 ..Default::default()
942 }),
943 ..Default::default()
944 }),
945 sensor: None,
946 touch: Some(fidl_input_report::TouchDescriptor {
947 input: Some(fidl_input_report::TouchInputDescriptor {
948 contacts: None,
949 max_contacts: None,
950 touch_type: None,
951 buttons: None,
952 ..Default::default()
953 }),
954 ..Default::default()
955 }),
956 keyboard: Some(fidl_input_report::KeyboardDescriptor {
957 input: Some(fidl_input_report::KeyboardInputDescriptor {
958 keys3: None,
959 ..Default::default()
960 }),
961 output: None,
962 ..Default::default()
963 }),
964 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
965 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
966 buttons: Some(vec![
967 fidl_input_report::ConsumerControlButton::VolumeUp,
968 fidl_input_report::ConsumerControlButton::VolumeDown,
969 ]),
970 ..Default::default()
971 }),
972 ..Default::default()
973 }),
974 ..Default::default()
975 });
976 }
977 _ => panic!("InputDevice handler received an unexpected request"),
978 }
979 });
980
981 let device_descriptor =
982 &input_device_proxy.get_descriptor().await.expect("Failed to get device descriptor");
983 assert!(is_device_type(&device_descriptor, InputDeviceType::ConsumerControls).await);
984 assert!(is_device_type(&device_descriptor, InputDeviceType::Mouse).await);
985 assert!(is_device_type(&device_descriptor, InputDeviceType::Touch).await);
986 assert!(is_device_type(&device_descriptor, InputDeviceType::Keyboard).await);
987 }
988
989 #[fuchsia::test]
990 fn unhandled_to_generic_conversion_sets_handled_flag_to_no() {
991 assert_eq!(
992 InputEvent::from(UnhandledInputEvent {
993 device_event: InputDeviceEvent::Fake,
994 device_descriptor: InputDeviceDescriptor::Fake,
995 event_time: zx::MonotonicInstant::from_nanos(1),
996 trace_id: None,
997 })
998 .handled,
999 Handled::No
1000 );
1001 }
1002
1003 #[fuchsia::test]
1004 fn unhandled_to_generic_conversion_preserves_fields() {
1005 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1006 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1007 assert_eq!(
1008 InputEvent::from(UnhandledInputEvent {
1009 device_event: InputDeviceEvent::Fake,
1010 device_descriptor: InputDeviceDescriptor::Fake,
1011 event_time: EVENT_TIME,
1012 trace_id: expected_trace_id,
1013 }),
1014 InputEvent {
1015 device_event: InputDeviceEvent::Fake,
1016 device_descriptor: InputDeviceDescriptor::Fake,
1017 event_time: EVENT_TIME,
1018 handled: Handled::No,
1019 trace_id: expected_trace_id,
1020 },
1021 );
1022 }
1023
1024 #[fuchsia::test]
1025 fn generic_to_unhandled_conversion_fails_for_handled_events() {
1026 assert_matches!(
1027 UnhandledInputEvent::try_from(InputEvent {
1028 device_event: InputDeviceEvent::Fake,
1029 device_descriptor: InputDeviceDescriptor::Fake,
1030 event_time: zx::MonotonicInstant::from_nanos(1),
1031 handled: Handled::Yes,
1032 trace_id: None,
1033 }),
1034 Err(_)
1035 )
1036 }
1037
1038 #[fuchsia::test]
1039 fn generic_to_unhandled_conversion_preserves_fields_for_unhandled_events() {
1040 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1041 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1042 assert_eq!(
1043 UnhandledInputEvent::try_from(InputEvent {
1044 device_event: InputDeviceEvent::Fake,
1045 device_descriptor: InputDeviceDescriptor::Fake,
1046 event_time: EVENT_TIME,
1047 handled: Handled::No,
1048 trace_id: expected_trace_id,
1049 })
1050 .unwrap(),
1051 UnhandledInputEvent {
1052 device_event: InputDeviceEvent::Fake,
1053 device_descriptor: InputDeviceDescriptor::Fake,
1054 event_time: EVENT_TIME,
1055 trace_id: expected_trace_id,
1056 },
1057 )
1058 }
1059
1060 #[test_case(Handled::No; "initially not handled")]
1061 #[test_case(Handled::Yes; "initially handled")]
1062 fn into_handled_if_yields_handled_yes_on_true(initially_handled: Handled) {
1063 let event = InputEvent {
1064 device_event: InputDeviceEvent::Fake,
1065 device_descriptor: InputDeviceDescriptor::Fake,
1066 event_time: zx::MonotonicInstant::from_nanos(1),
1067 handled: initially_handled,
1068 trace_id: None,
1069 };
1070 pretty_assertions::assert_eq!(event.into_handled_if(true).handled, Handled::Yes);
1071 }
1072
1073 #[test_case(Handled::No; "initially not handled")]
1074 #[test_case(Handled::Yes; "initially handled")]
1075 fn into_handled_if_leaves_handled_unchanged_on_false(initially_handled: Handled) {
1076 let event = InputEvent {
1077 device_event: InputDeviceEvent::Fake,
1078 device_descriptor: InputDeviceDescriptor::Fake,
1079 event_time: zx::MonotonicInstant::from_nanos(1),
1080 handled: initially_handled.clone(),
1081 trace_id: None,
1082 };
1083 pretty_assertions::assert_eq!(event.into_handled_if(false).handled, initially_handled);
1084 }
1085
1086 #[test_case(Handled::No; "initially not handled")]
1087 #[test_case(Handled::Yes; "initially handled")]
1088 fn into_handled_yields_handled_yes(initially_handled: Handled) {
1089 let event = InputEvent {
1090 device_event: InputDeviceEvent::Fake,
1091 device_descriptor: InputDeviceDescriptor::Fake,
1092 event_time: zx::MonotonicInstant::from_nanos(1),
1093 handled: initially_handled,
1094 trace_id: None,
1095 };
1096 pretty_assertions::assert_eq!(event.into_handled().handled, Handled::Yes);
1097 }
1098}