1use crate::{
6 consumer_controls_binding, keyboard_binding, light_sensor_binding, metrics, mouse_binding,
7 touch_binding,
8};
9use anyhow::{Error, format_err};
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
183impl UnhandledInputEvent {
184 pub fn get_event_type(&self) -> &'static str {
186 match self.device_event {
187 InputDeviceEvent::Keyboard(_) => "keyboard_event",
188 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
189 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
190 InputDeviceEvent::Mouse(_) => "mouse_event",
191 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
192 InputDeviceEvent::Touchpad(_) => "touchpad_event",
193 #[cfg(test)]
194 InputDeviceEvent::Fake => "fake_event",
195 }
196 }
197}
198
199#[derive(Clone, Debug, PartialEq)]
208pub enum InputDeviceEvent {
209 Keyboard(keyboard_binding::KeyboardEvent),
210 LightSensor(light_sensor_binding::LightSensorEvent),
211 ConsumerControls(consumer_controls_binding::ConsumerControlsEvent),
212 Mouse(mouse_binding::MouseEvent),
213 TouchScreen(touch_binding::TouchScreenEvent),
214 Touchpad(touch_binding::TouchpadEvent),
215 #[cfg(test)]
216 Fake,
217}
218
219#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
221pub enum InputEventType {
222 Keyboard,
223 LightSensor,
224 ConsumerControls,
225 Mouse,
226 TouchScreen,
227 Touchpad,
228 #[cfg(test)]
229 Fake,
230}
231
232impl From<&InputDeviceEvent> for InputEventType {
233 fn from(event: &InputDeviceEvent) -> Self {
234 match event {
235 InputDeviceEvent::Keyboard(_) => InputEventType::Keyboard,
236 InputDeviceEvent::LightSensor(_) => InputEventType::LightSensor,
237 InputDeviceEvent::ConsumerControls(_) => InputEventType::ConsumerControls,
238 InputDeviceEvent::Mouse(_) => InputEventType::Mouse,
239 InputDeviceEvent::TouchScreen(_) => InputEventType::TouchScreen,
240 InputDeviceEvent::Touchpad(_) => InputEventType::Touchpad,
241 #[cfg(test)]
242 InputDeviceEvent::Fake => InputEventType::Fake,
243 }
244 }
245}
246
247#[derive(Clone, Debug, PartialEq)]
257pub enum InputDeviceDescriptor {
258 Keyboard(keyboard_binding::KeyboardDeviceDescriptor),
259 LightSensor(light_sensor_binding::LightSensorDeviceDescriptor),
260 ConsumerControls(consumer_controls_binding::ConsumerControlsDeviceDescriptor),
261 Mouse(mouse_binding::MouseDeviceDescriptor),
262 TouchScreen(touch_binding::TouchScreenDeviceDescriptor),
263 Touchpad(touch_binding::TouchpadDeviceDescriptor),
264 #[cfg(test)]
265 Fake,
266}
267
268impl From<keyboard_binding::KeyboardDeviceDescriptor> for InputDeviceDescriptor {
269 fn from(b: keyboard_binding::KeyboardDeviceDescriptor) -> Self {
270 InputDeviceDescriptor::Keyboard(b)
271 }
272}
273
274#[derive(Copy, Clone, Debug, PartialEq)]
276pub enum Handled {
277 Yes,
279 No,
281}
282
283#[async_trait]
292pub trait InputDeviceBinding: Send {
293 fn get_device_descriptor(&self) -> InputDeviceDescriptor;
295
296 fn input_event_sender(&self) -> UnboundedSender<InputEvent>;
298}
299
300pub fn initialize_report_stream<InputDeviceProcessReportsFn>(
315 device_proxy: fidl_input_report::InputDeviceProxy,
316 device_descriptor: InputDeviceDescriptor,
317 mut event_sender: UnboundedSender<InputEvent>,
318 inspect_status: InputDeviceStatus,
319 metrics_logger: metrics::MetricsLogger,
320 mut process_reports: InputDeviceProcessReportsFn,
321) where
322 InputDeviceProcessReportsFn: 'static
323 + Send
324 + FnMut(
325 Vec<InputReport>,
326 Option<InputReport>,
327 &InputDeviceDescriptor,
328 &mut UnboundedSender<InputEvent>,
329 &InputDeviceStatus,
330 &metrics::MetricsLogger,
331 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>),
332{
333 fasync::Task::local(async move {
334 let mut previous_report: Option<InputReport> = None;
335 let (report_reader, server_end) = fidl::endpoints::create_proxy();
336 let result = device_proxy.get_input_reports_reader(server_end);
337 if result.is_err() {
338 metrics_logger.log_error(
339 InputPipelineErrorMetricDimensionEvent::InputDeviceGetInputReportsReaderError,
340 std::format!("error on GetInputReportsReader: {:?}", &result),
341 );
342 return; }
344 let mut report_stream = HangingGetStream::new(
345 report_reader,
346 fidl_input_report::InputReportsReaderProxy::read_input_reports,
347 );
348 loop {
349 match report_stream.next().await {
350 Some(Ok(Ok(input_reports))) => {
351 fuchsia_trace::duration!("input", "input-device-process-reports");
352 let (prev_report, inspect_receiver) = process_reports(
353 input_reports,
354 previous_report,
355 &device_descriptor,
356 &mut event_sender,
357 &inspect_status,
358 &metrics_logger,
359 );
360 previous_report = prev_report;
361
362 match inspect_receiver {
366 Some(mut receiver) => {
367 while let Some(event) = receiver.next().await {
368 inspect_status.count_generated_event(event);
369 }
370 }
371 None => (),
372 };
373 }
374 Some(Ok(Err(_service_error))) => break,
375 Some(Err(_fidl_error)) => break,
376 None => break,
377 }
378 }
379 log::warn!("initialize_report_stream exited - device binding no longer works");
382 })
383 .detach();
384}
385
386pub async fn is_device_type(
392 device_descriptor: &fidl_input_report::DeviceDescriptor,
393 device_type: InputDeviceType,
394) -> bool {
395 match device_type {
397 InputDeviceType::ConsumerControls => device_descriptor.consumer_control.is_some(),
398 InputDeviceType::Mouse => device_descriptor.mouse.is_some(),
399 InputDeviceType::Touch => device_descriptor.touch.is_some(),
400 InputDeviceType::Keyboard => device_descriptor.keyboard.is_some(),
401 InputDeviceType::LightSensor => device_descriptor.sensor.is_some(),
402 }
403}
404
405pub async fn get_device_binding(
413 device_type: InputDeviceType,
414 device_proxy: fidl_input_report::InputDeviceProxy,
415 device_id: u32,
416 input_event_sender: UnboundedSender<InputEvent>,
417 device_node: fuchsia_inspect::Node,
418 metrics_logger: metrics::MetricsLogger,
419) -> Result<Box<dyn InputDeviceBinding>, Error> {
420 match device_type {
421 InputDeviceType::ConsumerControls => {
422 let binding = consumer_controls_binding::ConsumerControlsBinding::new(
423 device_proxy,
424 device_id,
425 input_event_sender,
426 device_node,
427 metrics_logger,
428 )
429 .await?;
430 Ok(Box::new(binding))
431 }
432 InputDeviceType::Mouse => {
433 let binding = mouse_binding::MouseBinding::new(
434 device_proxy,
435 device_id,
436 input_event_sender,
437 device_node,
438 metrics_logger,
439 )
440 .await?;
441 Ok(Box::new(binding))
442 }
443 InputDeviceType::Touch => {
444 let binding = touch_binding::TouchBinding::new(
445 device_proxy,
446 device_id,
447 input_event_sender,
448 device_node,
449 metrics_logger,
450 )
451 .await?;
452 Ok(Box::new(binding))
453 }
454 InputDeviceType::Keyboard => {
455 let binding = keyboard_binding::KeyboardBinding::new(
456 device_proxy,
457 device_id,
458 input_event_sender,
459 device_node,
460 metrics_logger,
461 )
462 .await?;
463 Ok(Box::new(binding))
464 }
465 InputDeviceType::LightSensor => {
466 let binding = light_sensor_binding::LightSensorBinding::new(
467 device_proxy,
468 device_id,
469 input_event_sender,
470 device_node,
471 metrics_logger,
472 )
473 .await?;
474 Ok(Box::new(binding))
475 }
476 }
477}
478
479pub fn get_device_from_dir_entry_path(
488 dir_proxy: &fio::DirectoryProxy,
489 entry_path: &PathBuf,
490) -> Result<fidl_input_report::InputDeviceProxy, Error> {
491 let input_device_path = entry_path.to_str();
492 if input_device_path.is_none() {
493 return Err(format_err!("Failed to get entry path as a string."));
494 }
495
496 let (input_device, server) = fidl::endpoints::create_proxy::<InputDeviceMarker>();
497 fdio::service_connect_at(
498 dir_proxy.as_channel().as_ref(),
499 input_device_path.unwrap(),
500 server.into_channel(),
501 )
502 .expect("Failed to connect to InputDevice.");
503 Ok(input_device)
504}
505
506pub fn event_time_or_now(event_time: Option<i64>) -> zx::MonotonicInstant {
511 match event_time {
512 Some(time) => zx::MonotonicInstant::from_nanos(time),
513 None => zx::MonotonicInstant::get(),
514 }
515}
516
517impl std::convert::From<UnhandledInputEvent> for InputEvent {
518 fn from(event: UnhandledInputEvent) -> Self {
519 Self {
520 device_event: event.device_event,
521 device_descriptor: event.device_descriptor,
522 event_time: event.event_time,
523 handled: Handled::No,
524 trace_id: event.trace_id,
525 }
526 }
527}
528
529#[cfg(test)]
536impl std::convert::TryFrom<InputEvent> for UnhandledInputEvent {
537 type Error = anyhow::Error;
538 fn try_from(event: InputEvent) -> Result<UnhandledInputEvent, Self::Error> {
539 match event.handled {
540 Handled::Yes => {
541 Err(format_err!("Attempted to treat a handled InputEvent as unhandled"))
542 }
543 Handled::No => Ok(UnhandledInputEvent {
544 device_event: event.device_event,
545 device_descriptor: event.device_descriptor,
546 event_time: event.event_time,
547 trace_id: event.trace_id,
548 }),
549 }
550 }
551}
552
553impl InputEvent {
554 pub fn clone_with_wake_lease(&self) -> Self {
555 let device_event = match &self.device_event {
556 InputDeviceEvent::ConsumerControls(event) => {
557 InputDeviceEvent::ConsumerControls(event.clone_with_wake_lease())
558 }
559 InputDeviceEvent::Mouse(event) => {
560 InputDeviceEvent::Mouse(event.clone_with_wake_lease())
561 }
562 InputDeviceEvent::TouchScreen(event) => {
563 InputDeviceEvent::TouchScreen(event.clone_with_wake_lease())
564 }
565 _ => self.device_event.clone(),
566 };
567 Self {
568 device_event,
569 device_descriptor: self.device_descriptor.clone(),
570 event_time: self.event_time,
571 handled: self.handled,
572 trace_id: self.trace_id,
573 }
574 }
575
576 pub(crate) fn into_handled_if(self, predicate: bool) -> Self {
579 if predicate { Self { handled: Handled::Yes, ..self } } else { self }
580 }
581
582 pub(crate) fn into_handled(self) -> Self {
584 Self { handled: Handled::Yes, ..self }
585 }
586
587 pub fn into_with_event_time(self, event_time: zx::MonotonicInstant) -> Self {
589 Self { event_time, ..self }
590 }
591
592 #[cfg(test)]
594 pub fn into_with_device_descriptor(self, device_descriptor: InputDeviceDescriptor) -> Self {
595 Self { device_descriptor, ..self }
596 }
597
598 pub fn is_handled(&self) -> bool {
600 self.handled == Handled::Yes
601 }
602
603 pub fn get_event_type(&self) -> &'static str {
605 match self.device_event {
606 InputDeviceEvent::Keyboard(_) => "keyboard_event",
607 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
608 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
609 InputDeviceEvent::Mouse(_) => "mouse_event",
610 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
611 InputDeviceEvent::Touchpad(_) => "touchpad_event",
612 #[cfg(test)]
613 InputDeviceEvent::Fake => "fake_event",
614 }
615 }
616
617 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
618 node.record_int("event_time", self.event_time.into_nanos());
619 match &self.device_event {
620 InputDeviceEvent::LightSensor(e) => e.record_inspect(node),
621 InputDeviceEvent::ConsumerControls(e) => e.record_inspect(node),
622 InputDeviceEvent::Mouse(e) => e.record_inspect(node),
623 InputDeviceEvent::TouchScreen(e) => e.record_inspect(node),
624 InputDeviceEvent::Touchpad(e) => e.record_inspect(node),
625 InputDeviceEvent::Keyboard(_) => (),
627 #[cfg(test)] InputDeviceEvent::Fake => (),
629 }
630 }
631}
632
633#[cfg(test)]
634mod tests {
635 use super::*;
636 use assert_matches::assert_matches;
637 use diagnostics_assertions::AnyProperty;
638 use fidl_test_util::spawn_stream_handler;
639
640 use pretty_assertions::assert_eq;
641 use std::convert::TryFrom as _;
642 use test_case::test_case;
643
644 #[test]
645 fn max_event_time() {
646 let event_time = event_time_or_now(Some(i64::MAX));
647 assert_eq!(event_time, zx::MonotonicInstant::INFINITE);
648 }
649
650 #[test]
651 fn min_event_time() {
652 let event_time = event_time_or_now(Some(std::i64::MIN));
653 assert_eq!(event_time, zx::MonotonicInstant::INFINITE_PAST);
654 }
655
656 #[fasync::run_singlethreaded(test)]
657 async fn input_device_status_initialized_with_correct_properties() {
658 let inspector = fuchsia_inspect::Inspector::default();
659 let input_pipeline_node = inspector.root().create_child("input_pipeline");
660 let input_devices_node = input_pipeline_node.create_child("input_devices");
661 let device_node = input_devices_node.create_child("001_keyboard");
662 let _input_device_status = InputDeviceStatus::new(device_node);
663 diagnostics_assertions::assert_data_tree!(inspector, root: {
664 input_pipeline: {
665 input_devices: {
666 "001_keyboard": {
667 reports_received_count: 0u64,
668 reports_filtered_count: 0u64,
669 events_generated: 0u64,
670 last_received_timestamp_ns: 0u64,
671 last_generated_timestamp_ns: 0u64,
672 "fuchsia.inspect.Health": {
673 status: "STARTING_UP",
674 start_timestamp_nanos: AnyProperty
677 },
678 driver_to_binding_latency_ms: diagnostics_assertions::HistogramAssertion::exponential(super::LATENCY_HISTOGRAM_PROPERTIES),
679 }
680 }
681 }
682 });
683 }
684
685 #[test_case(i64::MIN; "min value")]
686 #[test_case(-1; "negative value")]
687 #[test_case(0; "zero")]
688 #[test_case(1; "positive value")]
689 #[test_case(i64::MAX; "max value")]
690 #[fuchsia::test(allow_stalls = false)]
691 async fn input_device_status_updates_latency_histogram_on_count_received_report(
692 latency_nsec: i64,
693 ) {
694 let mut expected_histogram = diagnostics_assertions::HistogramAssertion::exponential(
695 super::LATENCY_HISTOGRAM_PROPERTIES,
696 );
697 let inspector = fuchsia_inspect::Inspector::default();
698 let input_device_status = InputDeviceStatus::new_internal(
699 inspector.root().clone_weak(),
700 Box::new(move || zx::MonotonicInstant::from_nanos(latency_nsec)),
701 );
702 input_device_status
703 .count_received_report(&InputReport { event_time: Some(0), ..InputReport::default() });
704 expected_histogram.insert_values([latency_nsec / 1000 / 1000]);
705 diagnostics_assertions::assert_data_tree!(inspector, root: contains {
706 driver_to_binding_latency_ms: expected_histogram,
707 });
708 }
709
710 #[fasync::run_singlethreaded(test)]
713 async fn consumer_controls_input_device_exists() {
714 let input_device_proxy: fidl_input_report::InputDeviceProxy =
715 spawn_stream_handler(move |input_device_request| async move {
716 match input_device_request {
717 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
718 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
719 device_information: None,
720 mouse: None,
721 sensor: None,
722 touch: None,
723 keyboard: None,
724 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
725 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
726 buttons: Some(vec![
727 fidl_input_report::ConsumerControlButton::VolumeUp,
728 fidl_input_report::ConsumerControlButton::VolumeDown,
729 ]),
730 ..Default::default()
731 }),
732 ..Default::default()
733 }),
734 ..Default::default()
735 });
736 }
737 _ => panic!("InputDevice handler received an unexpected request"),
738 }
739 });
740
741 assert!(
742 is_device_type(
743 &input_device_proxy
744 .get_descriptor()
745 .await
746 .expect("Failed to get device descriptor"),
747 InputDeviceType::ConsumerControls
748 )
749 .await
750 );
751 }
752
753 #[fasync::run_singlethreaded(test)]
755 async fn mouse_input_device_exists() {
756 let input_device_proxy: fidl_input_report::InputDeviceProxy =
757 spawn_stream_handler(move |input_device_request| async move {
758 match input_device_request {
759 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
760 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
761 device_information: None,
762 mouse: Some(fidl_input_report::MouseDescriptor {
763 input: Some(fidl_input_report::MouseInputDescriptor {
764 movement_x: None,
765 movement_y: None,
766 position_x: None,
767 position_y: None,
768 scroll_v: None,
769 scroll_h: None,
770 buttons: None,
771 ..Default::default()
772 }),
773 ..Default::default()
774 }),
775 sensor: None,
776 touch: None,
777 keyboard: None,
778 consumer_control: None,
779 ..Default::default()
780 });
781 }
782 _ => panic!("InputDevice handler received an unexpected request"),
783 }
784 });
785
786 assert!(
787 is_device_type(
788 &input_device_proxy
789 .get_descriptor()
790 .await
791 .expect("Failed to get device descriptor"),
792 InputDeviceType::Mouse
793 )
794 .await
795 );
796 }
797
798 #[fasync::run_singlethreaded(test)]
801 async fn mouse_input_device_doesnt_exist() {
802 let input_device_proxy: fidl_input_report::InputDeviceProxy =
803 spawn_stream_handler(move |input_device_request| async move {
804 match input_device_request {
805 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
806 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
807 device_information: None,
808 mouse: None,
809 sensor: None,
810 touch: None,
811 keyboard: None,
812 consumer_control: None,
813 ..Default::default()
814 });
815 }
816 _ => panic!("InputDevice handler received an unexpected request"),
817 }
818 });
819
820 assert!(
821 !is_device_type(
822 &input_device_proxy
823 .get_descriptor()
824 .await
825 .expect("Failed to get device descriptor"),
826 InputDeviceType::Mouse
827 )
828 .await
829 );
830 }
831
832 #[fasync::run_singlethreaded(test)]
835 async fn touch_input_device_exists() {
836 let input_device_proxy: fidl_input_report::InputDeviceProxy =
837 spawn_stream_handler(move |input_device_request| async move {
838 match input_device_request {
839 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
840 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
841 device_information: None,
842 mouse: None,
843 sensor: None,
844 touch: Some(fidl_input_report::TouchDescriptor {
845 input: Some(fidl_input_report::TouchInputDescriptor {
846 contacts: None,
847 max_contacts: None,
848 touch_type: None,
849 buttons: None,
850 ..Default::default()
851 }),
852 ..Default::default()
853 }),
854 keyboard: None,
855 consumer_control: None,
856 ..Default::default()
857 });
858 }
859 _ => panic!("InputDevice handler received an unexpected request"),
860 }
861 });
862
863 assert!(
864 is_device_type(
865 &input_device_proxy
866 .get_descriptor()
867 .await
868 .expect("Failed to get device descriptor"),
869 InputDeviceType::Touch
870 )
871 .await
872 );
873 }
874
875 #[fasync::run_singlethreaded(test)]
878 async fn touch_input_device_doesnt_exist() {
879 let input_device_proxy: fidl_input_report::InputDeviceProxy =
880 spawn_stream_handler(move |input_device_request| async move {
881 match input_device_request {
882 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
883 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
884 device_information: None,
885 mouse: None,
886 sensor: None,
887 touch: None,
888 keyboard: None,
889 consumer_control: None,
890 ..Default::default()
891 });
892 }
893 _ => panic!("InputDevice handler received an unexpected request"),
894 }
895 });
896
897 assert!(
898 !is_device_type(
899 &input_device_proxy
900 .get_descriptor()
901 .await
902 .expect("Failed to get device descriptor"),
903 InputDeviceType::Touch
904 )
905 .await
906 );
907 }
908
909 #[fasync::run_singlethreaded(test)]
912 async fn keyboard_input_device_exists() {
913 let input_device_proxy: fidl_input_report::InputDeviceProxy =
914 spawn_stream_handler(move |input_device_request| async move {
915 match input_device_request {
916 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
917 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
918 device_information: None,
919 mouse: None,
920 sensor: None,
921 touch: None,
922 keyboard: Some(fidl_input_report::KeyboardDescriptor {
923 input: Some(fidl_input_report::KeyboardInputDescriptor {
924 keys3: None,
925 ..Default::default()
926 }),
927 output: None,
928 ..Default::default()
929 }),
930 consumer_control: None,
931 ..Default::default()
932 });
933 }
934 _ => panic!("InputDevice handler received an unexpected request"),
935 }
936 });
937
938 assert!(
939 is_device_type(
940 &input_device_proxy
941 .get_descriptor()
942 .await
943 .expect("Failed to get device descriptor"),
944 InputDeviceType::Keyboard
945 )
946 .await
947 );
948 }
949
950 #[fasync::run_singlethreaded(test)]
953 async fn keyboard_input_device_doesnt_exist() {
954 let input_device_proxy: fidl_input_report::InputDeviceProxy =
955 spawn_stream_handler(move |input_device_request| async move {
956 match input_device_request {
957 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
958 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
959 device_information: None,
960 mouse: None,
961 sensor: None,
962 touch: None,
963 keyboard: None,
964 consumer_control: None,
965 ..Default::default()
966 });
967 }
968 _ => panic!("InputDevice handler received an unexpected request"),
969 }
970 });
971
972 assert!(
973 !is_device_type(
974 &input_device_proxy
975 .get_descriptor()
976 .await
977 .expect("Failed to get device descriptor"),
978 InputDeviceType::Keyboard
979 )
980 .await
981 );
982 }
983
984 #[fasync::run_singlethreaded(test)]
986 async fn no_input_device_match() {
987 let input_device_proxy: fidl_input_report::InputDeviceProxy =
988 spawn_stream_handler(move |input_device_request| async move {
989 match input_device_request {
990 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
991 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
992 device_information: None,
993 mouse: Some(fidl_input_report::MouseDescriptor {
994 input: Some(fidl_input_report::MouseInputDescriptor {
995 movement_x: None,
996 movement_y: None,
997 position_x: None,
998 position_y: None,
999 scroll_v: None,
1000 scroll_h: None,
1001 buttons: None,
1002 ..Default::default()
1003 }),
1004 ..Default::default()
1005 }),
1006 sensor: None,
1007 touch: Some(fidl_input_report::TouchDescriptor {
1008 input: Some(fidl_input_report::TouchInputDescriptor {
1009 contacts: None,
1010 max_contacts: None,
1011 touch_type: None,
1012 buttons: None,
1013 ..Default::default()
1014 }),
1015 ..Default::default()
1016 }),
1017 keyboard: Some(fidl_input_report::KeyboardDescriptor {
1018 input: Some(fidl_input_report::KeyboardInputDescriptor {
1019 keys3: None,
1020 ..Default::default()
1021 }),
1022 output: None,
1023 ..Default::default()
1024 }),
1025 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
1026 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
1027 buttons: Some(vec![
1028 fidl_input_report::ConsumerControlButton::VolumeUp,
1029 fidl_input_report::ConsumerControlButton::VolumeDown,
1030 ]),
1031 ..Default::default()
1032 }),
1033 ..Default::default()
1034 }),
1035 ..Default::default()
1036 });
1037 }
1038 _ => panic!("InputDevice handler received an unexpected request"),
1039 }
1040 });
1041
1042 let device_descriptor =
1043 &input_device_proxy.get_descriptor().await.expect("Failed to get device descriptor");
1044 assert!(is_device_type(&device_descriptor, InputDeviceType::ConsumerControls).await);
1045 assert!(is_device_type(&device_descriptor, InputDeviceType::Mouse).await);
1046 assert!(is_device_type(&device_descriptor, InputDeviceType::Touch).await);
1047 assert!(is_device_type(&device_descriptor, InputDeviceType::Keyboard).await);
1048 }
1049
1050 #[fuchsia::test]
1051 fn unhandled_to_generic_conversion_sets_handled_flag_to_no() {
1052 assert_eq!(
1053 InputEvent::from(UnhandledInputEvent {
1054 device_event: InputDeviceEvent::Fake,
1055 device_descriptor: InputDeviceDescriptor::Fake,
1056 event_time: zx::MonotonicInstant::from_nanos(1),
1057 trace_id: None,
1058 })
1059 .handled,
1060 Handled::No
1061 );
1062 }
1063
1064 #[fuchsia::test]
1065 fn unhandled_to_generic_conversion_preserves_fields() {
1066 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1067 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1068 assert_eq!(
1069 InputEvent::from(UnhandledInputEvent {
1070 device_event: InputDeviceEvent::Fake,
1071 device_descriptor: InputDeviceDescriptor::Fake,
1072 event_time: EVENT_TIME,
1073 trace_id: expected_trace_id,
1074 }),
1075 InputEvent {
1076 device_event: InputDeviceEvent::Fake,
1077 device_descriptor: InputDeviceDescriptor::Fake,
1078 event_time: EVENT_TIME,
1079 handled: Handled::No,
1080 trace_id: expected_trace_id,
1081 },
1082 );
1083 }
1084
1085 #[fuchsia::test]
1086 fn generic_to_unhandled_conversion_fails_for_handled_events() {
1087 assert_matches!(
1088 UnhandledInputEvent::try_from(InputEvent {
1089 device_event: InputDeviceEvent::Fake,
1090 device_descriptor: InputDeviceDescriptor::Fake,
1091 event_time: zx::MonotonicInstant::from_nanos(1),
1092 handled: Handled::Yes,
1093 trace_id: None,
1094 }),
1095 Err(_)
1096 )
1097 }
1098
1099 #[fuchsia::test]
1100 fn generic_to_unhandled_conversion_preserves_fields_for_unhandled_events() {
1101 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1102 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1103 assert_eq!(
1104 UnhandledInputEvent::try_from(InputEvent {
1105 device_event: InputDeviceEvent::Fake,
1106 device_descriptor: InputDeviceDescriptor::Fake,
1107 event_time: EVENT_TIME,
1108 handled: Handled::No,
1109 trace_id: expected_trace_id,
1110 })
1111 .unwrap(),
1112 UnhandledInputEvent {
1113 device_event: InputDeviceEvent::Fake,
1114 device_descriptor: InputDeviceDescriptor::Fake,
1115 event_time: EVENT_TIME,
1116 trace_id: expected_trace_id,
1117 },
1118 )
1119 }
1120
1121 #[test_case(Handled::No; "initially not handled")]
1122 #[test_case(Handled::Yes; "initially handled")]
1123 fn into_handled_if_yields_handled_yes_on_true(initially_handled: Handled) {
1124 let event = InputEvent {
1125 device_event: InputDeviceEvent::Fake,
1126 device_descriptor: InputDeviceDescriptor::Fake,
1127 event_time: zx::MonotonicInstant::from_nanos(1),
1128 handled: initially_handled,
1129 trace_id: None,
1130 };
1131 pretty_assertions::assert_eq!(event.into_handled_if(true).handled, Handled::Yes);
1132 }
1133
1134 #[test_case(Handled::No; "initially not handled")]
1135 #[test_case(Handled::Yes; "initially handled")]
1136 fn into_handled_if_leaves_handled_unchanged_on_false(initially_handled: Handled) {
1137 let event = InputEvent {
1138 device_event: InputDeviceEvent::Fake,
1139 device_descriptor: InputDeviceDescriptor::Fake,
1140 event_time: zx::MonotonicInstant::from_nanos(1),
1141 handled: initially_handled.clone(),
1142 trace_id: None,
1143 };
1144 pretty_assertions::assert_eq!(event.into_handled_if(false).handled, initially_handled);
1145 }
1146
1147 #[test_case(Handled::No; "initially not handled")]
1148 #[test_case(Handled::Yes; "initially handled")]
1149 fn into_handled_yields_handled_yes(initially_handled: Handled) {
1150 let event = InputEvent {
1151 device_event: InputDeviceEvent::Fake,
1152 device_descriptor: InputDeviceDescriptor::Fake,
1153 event_time: zx::MonotonicInstant::from_nanos(1),
1154 handled: initially_handled,
1155 trace_id: None,
1156 };
1157 pretty_assertions::assert_eq!(event.into_handled().handled, Handled::Yes);
1158 }
1159}