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