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
29#[derive(Debug, Clone, Default)]
30pub struct InputPipelineFeatureFlags {
31 pub enable_merge_touch_events: bool,
33}
34
35pub static INPUT_REPORT_PATH: &str = "/dev/class/input-report";
37
38const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
39 floor: 0,
40 initial_step: 1,
41 step_multiplier: 10,
42 buckets: 7,
53};
54
55pub struct InputDeviceStatus {
58 now: Box<dyn Fn() -> zx::MonotonicInstant>,
61
62 _node: fuchsia_inspect::Node,
64
65 reports_received_count: fuchsia_inspect::UintProperty,
67
68 reports_filtered_count: fuchsia_inspect::UintProperty,
71
72 events_generated: fuchsia_inspect::UintProperty,
75
76 last_received_timestamp_ns: fuchsia_inspect::UintProperty,
78
79 last_generated_timestamp_ns: fuchsia_inspect::UintProperty,
81
82 pub health_node: fuchsia_inspect::health::Node,
84
85 driver_to_binding_latency_ms: fuchsia_inspect::IntExponentialHistogramProperty,
90}
91
92impl InputDeviceStatus {
93 pub fn new(device_node: fuchsia_inspect::Node) -> Self {
94 Self::new_internal(device_node, Box::new(zx::MonotonicInstant::get))
95 }
96
97 fn new_internal(
98 device_node: fuchsia_inspect::Node,
99 now: Box<dyn Fn() -> zx::MonotonicInstant>,
100 ) -> Self {
101 let mut health_node = fuchsia_inspect::health::Node::new(&device_node);
102 health_node.set_starting_up();
103
104 let reports_received_count = device_node.create_uint("reports_received_count", 0);
105 let reports_filtered_count = device_node.create_uint("reports_filtered_count", 0);
106 let events_generated = device_node.create_uint("events_generated", 0);
107 let last_received_timestamp_ns = device_node.create_uint("last_received_timestamp_ns", 0);
108 let last_generated_timestamp_ns = device_node.create_uint("last_generated_timestamp_ns", 0);
109 let driver_to_binding_latency_ms = device_node.create_int_exponential_histogram(
110 "driver_to_binding_latency_ms",
111 LATENCY_HISTOGRAM_PROPERTIES,
112 );
113
114 Self {
115 now,
116 _node: device_node,
117 reports_received_count,
118 reports_filtered_count,
119 events_generated,
120 last_received_timestamp_ns,
121 last_generated_timestamp_ns,
122 health_node,
123 driver_to_binding_latency_ms,
124 }
125 }
126
127 pub fn count_received_report(&self, report: &InputReport) {
128 self.reports_received_count.add(1);
129 match report.event_time {
130 Some(event_time) => {
131 self.driver_to_binding_latency_ms.insert(
132 ((self.now)() - zx::MonotonicInstant::from_nanos(event_time)).into_millis(),
133 );
134 self.last_received_timestamp_ns.set(event_time.try_into().unwrap());
135 }
136 None => (),
137 }
138 }
139
140 pub fn count_filtered_report(&self) {
141 self.reports_filtered_count.add(1);
142 }
143
144 pub fn count_generated_event(&self, event: InputEvent) {
145 self.events_generated.add(1);
146 self.last_generated_timestamp_ns.set(event.event_time.into_nanos().try_into().unwrap());
147 }
148
149 pub fn count_generated_events(&self, events: &Vec<InputEvent>) {
150 self.events_generated.add(events.len() as u64);
151 if let Some(last_event) = events.last() {
152 self.last_generated_timestamp_ns
153 .set(last_event.event_time.into_nanos().try_into().unwrap());
154 }
155 }
156}
157
158#[derive(Clone, Debug, PartialEq)]
160pub struct InputEvent {
161 pub device_event: InputDeviceEvent,
163
164 pub device_descriptor: InputDeviceDescriptor,
167
168 pub event_time: zx::MonotonicInstant,
170
171 pub handled: Handled,
173
174 pub trace_id: Option<ftrace::Id>,
175}
176
177#[derive(Clone, Debug, PartialEq)]
183pub struct UnhandledInputEvent {
184 pub device_event: InputDeviceEvent,
186
187 pub device_descriptor: InputDeviceDescriptor,
190
191 pub event_time: zx::MonotonicInstant,
193
194 pub trace_id: Option<ftrace::Id>,
195}
196
197impl UnhandledInputEvent {
198 pub fn get_event_type(&self) -> &'static str {
200 match self.device_event {
201 InputDeviceEvent::Keyboard(_) => "keyboard_event",
202 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
203 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
204 InputDeviceEvent::Mouse(_) => "mouse_event",
205 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
206 InputDeviceEvent::Touchpad(_) => "touchpad_event",
207 #[cfg(test)]
208 InputDeviceEvent::Fake => "fake_event",
209 }
210 }
211}
212
213#[derive(Clone, Debug, PartialEq)]
222pub enum InputDeviceEvent {
223 Keyboard(keyboard_binding::KeyboardEvent),
224 LightSensor(light_sensor_binding::LightSensorEvent),
225 ConsumerControls(consumer_controls_binding::ConsumerControlsEvent),
226 Mouse(mouse_binding::MouseEvent),
227 TouchScreen(touch_binding::TouchScreenEvent),
228 Touchpad(touch_binding::TouchpadEvent),
229 #[cfg(test)]
230 Fake,
231}
232
233#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
235pub enum InputEventType {
236 Keyboard,
237 LightSensor,
238 ConsumerControls,
239 Mouse,
240 TouchScreen,
241 Touchpad,
242 #[cfg(test)]
243 Fake,
244}
245
246impl std::fmt::Display for InputEventType {
247 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 match &*self {
249 InputEventType::Keyboard => write!(f, "keyboard"),
250 InputEventType::LightSensor => write!(f, "light_sensor"),
251 InputEventType::ConsumerControls => write!(f, "consumer_controls"),
252 InputEventType::Mouse => write!(f, "mouse"),
253 InputEventType::TouchScreen => write!(f, "touch_screen"),
254 InputEventType::Touchpad => write!(f, "touchpad"),
255 #[cfg(test)]
256 InputEventType::Fake => write!(f, "fake"),
257 }
258 }
259}
260
261impl From<&InputDeviceEvent> for InputEventType {
262 fn from(event: &InputDeviceEvent) -> Self {
263 match event {
264 InputDeviceEvent::Keyboard(_) => InputEventType::Keyboard,
265 InputDeviceEvent::LightSensor(_) => InputEventType::LightSensor,
266 InputDeviceEvent::ConsumerControls(_) => InputEventType::ConsumerControls,
267 InputDeviceEvent::Mouse(_) => InputEventType::Mouse,
268 InputDeviceEvent::TouchScreen(_) => InputEventType::TouchScreen,
269 InputDeviceEvent::Touchpad(_) => InputEventType::Touchpad,
270 #[cfg(test)]
271 InputDeviceEvent::Fake => InputEventType::Fake,
272 }
273 }
274}
275
276#[derive(Clone, Debug, PartialEq)]
286pub enum InputDeviceDescriptor {
287 Keyboard(keyboard_binding::KeyboardDeviceDescriptor),
288 LightSensor(light_sensor_binding::LightSensorDeviceDescriptor),
289 ConsumerControls(consumer_controls_binding::ConsumerControlsDeviceDescriptor),
290 Mouse(mouse_binding::MouseDeviceDescriptor),
291 TouchScreen(touch_binding::TouchScreenDeviceDescriptor),
292 Touchpad(touch_binding::TouchpadDeviceDescriptor),
293 #[cfg(test)]
294 Fake,
295}
296
297impl From<keyboard_binding::KeyboardDeviceDescriptor> for InputDeviceDescriptor {
298 fn from(b: keyboard_binding::KeyboardDeviceDescriptor) -> Self {
299 InputDeviceDescriptor::Keyboard(b)
300 }
301}
302
303impl InputDeviceDescriptor {
304 pub fn device_id(&self) -> u32 {
305 match self {
306 InputDeviceDescriptor::Keyboard(b) => b.device_id,
307 InputDeviceDescriptor::LightSensor(b) => b.device_id,
308 InputDeviceDescriptor::ConsumerControls(b) => b.device_id,
309 InputDeviceDescriptor::Mouse(b) => b.device_id,
310 InputDeviceDescriptor::TouchScreen(b) => b.device_id,
311 InputDeviceDescriptor::Touchpad(b) => b.device_id,
312 #[cfg(test)]
313 InputDeviceDescriptor::Fake => 0,
314 }
315 }
316}
317
318#[derive(Copy, Clone, Debug, PartialEq)]
320pub enum Handled {
321 Yes,
323 No,
325}
326
327#[async_trait]
336pub trait InputDeviceBinding: Send {
337 fn get_device_descriptor(&self) -> InputDeviceDescriptor;
339
340 fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>>;
342}
343
344pub fn initialize_report_stream<InputDeviceProcessReportsFn>(
361 device_proxy: fidl_input_report::InputDeviceProxy,
362 device_descriptor: InputDeviceDescriptor,
363 mut event_sender: UnboundedSender<Vec<InputEvent>>,
364 inspect_status: InputDeviceStatus,
365 metrics_logger: metrics::MetricsLogger,
366 feature_flags: InputPipelineFeatureFlags,
367 mut process_reports: InputDeviceProcessReportsFn,
368) where
369 InputDeviceProcessReportsFn: 'static
370 + Send
371 + FnMut(
372 Vec<InputReport>,
373 Option<InputReport>,
374 &InputDeviceDescriptor,
375 &mut UnboundedSender<Vec<InputEvent>>,
376 &InputDeviceStatus,
377 &metrics::MetricsLogger,
378 &InputPipelineFeatureFlags,
379 ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>),
380{
381 fasync::Task::local(async move {
382 let mut previous_report: Option<InputReport> = None;
383 let (report_reader, server_end) = fidl::endpoints::create_proxy();
384 let result = device_proxy.get_input_reports_reader(server_end);
385 if result.is_err() {
386 metrics_logger.log_error(
387 InputPipelineErrorMetricDimensionEvent::InputDeviceGetInputReportsReaderError,
388 std::format!("error on GetInputReportsReader: {:?}", &result),
389 );
390 return; }
392 let mut report_stream = HangingGetStream::new(
393 report_reader,
394 fidl_input_report::InputReportsReaderProxy::read_input_reports,
395 );
396 loop {
397 match report_stream.next().await {
398 Some(Ok(Ok(input_reports))) => {
399 fuchsia_trace::duration!("input", "input-device-process-reports");
400 let (prev_report, inspect_receiver) = process_reports(
401 input_reports,
402 previous_report,
403 &device_descriptor,
404 &mut event_sender,
405 &inspect_status,
406 &metrics_logger,
407 &feature_flags,
408 );
409 previous_report = prev_report;
410
411 if let Some(previous_report) = previous_report.as_ref() {
412 debug_assert!(
413 previous_report.wake_lease.is_none(),
414 "previous_report must not have a wake lease"
415 );
416 }
417
418 match inspect_receiver {
422 Some(mut receiver) => {
423 while let Some(event) = receiver.next().await {
424 inspect_status.count_generated_event(event);
425 }
426 }
427 None => (),
428 };
429 }
430 Some(Ok(Err(_service_error))) => break,
431 Some(Err(_fidl_error)) => break,
432 None => break,
433 }
434 }
435 log::warn!("initialize_report_stream exited - device binding no longer works");
438 })
439 .detach();
440}
441
442pub async fn is_device_type(
448 device_descriptor: &fidl_input_report::DeviceDescriptor,
449 device_type: InputDeviceType,
450) -> bool {
451 match device_type {
453 InputDeviceType::ConsumerControls => device_descriptor.consumer_control.is_some(),
454 InputDeviceType::Mouse => device_descriptor.mouse.is_some(),
455 InputDeviceType::Touch => device_descriptor.touch.is_some(),
456 InputDeviceType::Keyboard => device_descriptor.keyboard.is_some(),
457 InputDeviceType::LightSensor => device_descriptor.sensor.is_some(),
458 }
459}
460
461pub async fn get_device_binding(
469 device_type: InputDeviceType,
470 device_proxy: fidl_input_report::InputDeviceProxy,
471 device_id: u32,
472 input_event_sender: UnboundedSender<Vec<InputEvent>>,
473 device_node: fuchsia_inspect::Node,
474 feature_flags: InputPipelineFeatureFlags,
475 metrics_logger: metrics::MetricsLogger,
476) -> Result<Box<dyn InputDeviceBinding>, Error> {
477 match device_type {
478 InputDeviceType::ConsumerControls => {
479 let binding = consumer_controls_binding::ConsumerControlsBinding::new(
480 device_proxy,
481 device_id,
482 input_event_sender,
483 device_node,
484 feature_flags.clone(),
485 metrics_logger,
486 )
487 .await?;
488 Ok(Box::new(binding))
489 }
490 InputDeviceType::Mouse => {
491 let binding = mouse_binding::MouseBinding::new(
492 device_proxy,
493 device_id,
494 input_event_sender,
495 device_node,
496 feature_flags.clone(),
497 metrics_logger,
498 )
499 .await?;
500 Ok(Box::new(binding))
501 }
502 InputDeviceType::Touch => {
503 let binding = touch_binding::TouchBinding::new(
504 device_proxy,
505 device_id,
506 input_event_sender,
507 device_node,
508 feature_flags.clone(),
509 metrics_logger,
510 )
511 .await?;
512 Ok(Box::new(binding))
513 }
514 InputDeviceType::Keyboard => {
515 let binding = keyboard_binding::KeyboardBinding::new(
516 device_proxy,
517 device_id,
518 input_event_sender,
519 device_node,
520 feature_flags.clone(),
521 metrics_logger,
522 )
523 .await?;
524 Ok(Box::new(binding))
525 }
526 InputDeviceType::LightSensor => {
527 let binding = light_sensor_binding::LightSensorBinding::new(
528 device_proxy,
529 device_id,
530 input_event_sender,
531 device_node,
532 feature_flags.clone(),
533 metrics_logger,
534 )
535 .await?;
536 Ok(Box::new(binding))
537 }
538 }
539}
540
541pub fn get_device_from_dir_entry_path(
550 dir_proxy: &fio::DirectoryProxy,
551 entry_path: &PathBuf,
552) -> Result<fidl_input_report::InputDeviceProxy, Error> {
553 let input_device_path = entry_path.to_str();
554 if input_device_path.is_none() {
555 return Err(format_err!("Failed to get entry path as a string."));
556 }
557
558 let (input_device, server) = fidl::endpoints::create_proxy::<InputDeviceMarker>();
559 fdio::service_connect_at(
560 dir_proxy.as_channel().as_ref(),
561 input_device_path.unwrap(),
562 server.into_channel(),
563 )
564 .expect("Failed to connect to InputDevice.");
565 Ok(input_device)
566}
567
568pub fn event_time_or_now(event_time: Option<i64>) -> zx::MonotonicInstant {
573 match event_time {
574 Some(time) => zx::MonotonicInstant::from_nanos(time),
575 None => zx::MonotonicInstant::get(),
576 }
577}
578
579impl std::convert::From<UnhandledInputEvent> for InputEvent {
580 fn from(event: UnhandledInputEvent) -> Self {
581 Self {
582 device_event: event.device_event,
583 device_descriptor: event.device_descriptor,
584 event_time: event.event_time,
585 handled: Handled::No,
586 trace_id: event.trace_id,
587 }
588 }
589}
590
591#[cfg(test)]
598impl std::convert::TryFrom<InputEvent> for UnhandledInputEvent {
599 type Error = anyhow::Error;
600 fn try_from(event: InputEvent) -> Result<UnhandledInputEvent, Self::Error> {
601 match event.handled {
602 Handled::Yes => {
603 Err(format_err!("Attempted to treat a handled InputEvent as unhandled"))
604 }
605 Handled::No => Ok(UnhandledInputEvent {
606 device_event: event.device_event,
607 device_descriptor: event.device_descriptor,
608 event_time: event.event_time,
609 trace_id: event.trace_id,
610 }),
611 }
612 }
613}
614
615impl InputEvent {
616 pub fn clone_with_wake_lease(&self) -> Self {
617 let device_event = match &self.device_event {
618 InputDeviceEvent::ConsumerControls(event) => {
619 InputDeviceEvent::ConsumerControls(event.clone_with_wake_lease())
620 }
621 InputDeviceEvent::Mouse(event) => {
622 InputDeviceEvent::Mouse(event.clone_with_wake_lease())
623 }
624 InputDeviceEvent::TouchScreen(event) => {
625 InputDeviceEvent::TouchScreen(event.clone_with_wake_lease())
626 }
627 _ => self.device_event.clone(),
628 };
629 Self {
630 device_event,
631 device_descriptor: self.device_descriptor.clone(),
632 event_time: self.event_time,
633 handled: self.handled,
634 trace_id: self.trace_id,
635 }
636 }
637
638 pub(crate) fn into_handled_if(self, predicate: bool) -> Self {
641 if predicate { Self { handled: Handled::Yes, ..self } } else { self }
642 }
643
644 pub(crate) fn into_handled(self) -> Self {
646 Self { handled: Handled::Yes, ..self }
647 }
648
649 pub fn into_with_event_time(self, event_time: zx::MonotonicInstant) -> Self {
651 Self { event_time, ..self }
652 }
653
654 #[cfg(test)]
656 pub fn into_with_device_descriptor(self, device_descriptor: InputDeviceDescriptor) -> Self {
657 Self { device_descriptor, ..self }
658 }
659
660 pub fn is_handled(&self) -> bool {
662 self.handled == Handled::Yes
663 }
664
665 pub fn get_event_type(&self) -> &'static str {
667 match self.device_event {
668 InputDeviceEvent::Keyboard(_) => "keyboard_event",
669 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
670 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
671 InputDeviceEvent::Mouse(_) => "mouse_event",
672 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
673 InputDeviceEvent::Touchpad(_) => "touchpad_event",
674 #[cfg(test)]
675 InputDeviceEvent::Fake => "fake_event",
676 }
677 }
678
679 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
680 node.record_int("event_time", self.event_time.into_nanos());
681 match &self.device_event {
682 InputDeviceEvent::LightSensor(e) => e.record_inspect(node),
683 InputDeviceEvent::ConsumerControls(e) => e.record_inspect(node),
684 InputDeviceEvent::Mouse(e) => e.record_inspect(node),
685 InputDeviceEvent::TouchScreen(e) => e.record_inspect(node),
686 InputDeviceEvent::Touchpad(e) => e.record_inspect(node),
687 InputDeviceEvent::Keyboard(_) => (),
689 #[cfg(test)] InputDeviceEvent::Fake => (),
691 }
692 }
693}
694
695#[cfg(test)]
696mod tests {
697 use super::*;
698 use assert_matches::assert_matches;
699 use diagnostics_assertions::AnyProperty;
700 use fidl_test_util::spawn_stream_handler;
701
702 use pretty_assertions::assert_eq;
703 use std::convert::TryFrom as _;
704 use test_case::test_case;
705
706 #[test]
707 fn max_event_time() {
708 let event_time = event_time_or_now(Some(i64::MAX));
709 assert_eq!(event_time, zx::MonotonicInstant::INFINITE);
710 }
711
712 #[test]
713 fn min_event_time() {
714 let event_time = event_time_or_now(Some(std::i64::MIN));
715 assert_eq!(event_time, zx::MonotonicInstant::INFINITE_PAST);
716 }
717
718 #[fasync::run_singlethreaded(test)]
719 async fn input_device_status_initialized_with_correct_properties() {
720 let inspector = fuchsia_inspect::Inspector::default();
721 let input_pipeline_node = inspector.root().create_child("input_pipeline");
722 let input_devices_node = input_pipeline_node.create_child("input_devices");
723 let device_node = input_devices_node.create_child("001_keyboard");
724 let _input_device_status = InputDeviceStatus::new(device_node);
725 diagnostics_assertions::assert_data_tree!(inspector, root: {
726 input_pipeline: {
727 input_devices: {
728 "001_keyboard": {
729 reports_received_count: 0u64,
730 reports_filtered_count: 0u64,
731 events_generated: 0u64,
732 last_received_timestamp_ns: 0u64,
733 last_generated_timestamp_ns: 0u64,
734 "fuchsia.inspect.Health": {
735 status: "STARTING_UP",
736 start_timestamp_nanos: AnyProperty
739 },
740 driver_to_binding_latency_ms: diagnostics_assertions::HistogramAssertion::exponential(super::LATENCY_HISTOGRAM_PROPERTIES),
741 }
742 }
743 }
744 });
745 }
746
747 #[test_case(i64::MIN; "min value")]
748 #[test_case(-1; "negative value")]
749 #[test_case(0; "zero")]
750 #[test_case(1; "positive value")]
751 #[test_case(i64::MAX; "max value")]
752 #[fuchsia::test(allow_stalls = false)]
753 async fn input_device_status_updates_latency_histogram_on_count_received_report(
754 latency_nsec: i64,
755 ) {
756 let mut expected_histogram = diagnostics_assertions::HistogramAssertion::exponential(
757 super::LATENCY_HISTOGRAM_PROPERTIES,
758 );
759 let inspector = fuchsia_inspect::Inspector::default();
760 let input_device_status = InputDeviceStatus::new_internal(
761 inspector.root().clone_weak(),
762 Box::new(move || zx::MonotonicInstant::from_nanos(latency_nsec)),
763 );
764 input_device_status
765 .count_received_report(&InputReport { event_time: Some(0), ..InputReport::default() });
766 expected_histogram.insert_values([latency_nsec / 1000 / 1000]);
767 diagnostics_assertions::assert_data_tree!(inspector, root: contains {
768 driver_to_binding_latency_ms: expected_histogram,
769 });
770 }
771
772 #[fasync::run_singlethreaded(test)]
775 async fn consumer_controls_input_device_exists() {
776 let input_device_proxy: fidl_input_report::InputDeviceProxy =
777 spawn_stream_handler(move |input_device_request| async move {
778 match input_device_request {
779 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
780 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
781 device_information: None,
782 mouse: None,
783 sensor: None,
784 touch: None,
785 keyboard: None,
786 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
787 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
788 buttons: Some(vec![
789 fidl_input_report::ConsumerControlButton::VolumeUp,
790 fidl_input_report::ConsumerControlButton::VolumeDown,
791 ]),
792 ..Default::default()
793 }),
794 ..Default::default()
795 }),
796 ..Default::default()
797 });
798 }
799 _ => panic!("InputDevice handler received an unexpected request"),
800 }
801 });
802
803 assert!(
804 is_device_type(
805 &input_device_proxy
806 .get_descriptor()
807 .await
808 .expect("Failed to get device descriptor"),
809 InputDeviceType::ConsumerControls
810 )
811 .await
812 );
813 }
814
815 #[fasync::run_singlethreaded(test)]
817 async fn mouse_input_device_exists() {
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: Some(fidl_input_report::MouseDescriptor {
825 input: Some(fidl_input_report::MouseInputDescriptor {
826 movement_x: None,
827 movement_y: None,
828 position_x: None,
829 position_y: None,
830 scroll_v: None,
831 scroll_h: None,
832 buttons: None,
833 ..Default::default()
834 }),
835 ..Default::default()
836 }),
837 sensor: None,
838 touch: None,
839 keyboard: None,
840 consumer_control: None,
841 ..Default::default()
842 });
843 }
844 _ => panic!("InputDevice handler received an unexpected request"),
845 }
846 });
847
848 assert!(
849 is_device_type(
850 &input_device_proxy
851 .get_descriptor()
852 .await
853 .expect("Failed to get device descriptor"),
854 InputDeviceType::Mouse
855 )
856 .await
857 );
858 }
859
860 #[fasync::run_singlethreaded(test)]
863 async fn mouse_input_device_doesnt_exist() {
864 let input_device_proxy: fidl_input_report::InputDeviceProxy =
865 spawn_stream_handler(move |input_device_request| async move {
866 match input_device_request {
867 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
868 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
869 device_information: None,
870 mouse: None,
871 sensor: None,
872 touch: None,
873 keyboard: None,
874 consumer_control: None,
875 ..Default::default()
876 });
877 }
878 _ => panic!("InputDevice handler received an unexpected request"),
879 }
880 });
881
882 assert!(
883 !is_device_type(
884 &input_device_proxy
885 .get_descriptor()
886 .await
887 .expect("Failed to get device descriptor"),
888 InputDeviceType::Mouse
889 )
890 .await
891 );
892 }
893
894 #[fasync::run_singlethreaded(test)]
897 async fn touch_input_device_exists() {
898 let input_device_proxy: fidl_input_report::InputDeviceProxy =
899 spawn_stream_handler(move |input_device_request| async move {
900 match input_device_request {
901 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
902 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
903 device_information: None,
904 mouse: None,
905 sensor: None,
906 touch: Some(fidl_input_report::TouchDescriptor {
907 input: Some(fidl_input_report::TouchInputDescriptor {
908 contacts: None,
909 max_contacts: None,
910 touch_type: None,
911 buttons: None,
912 ..Default::default()
913 }),
914 ..Default::default()
915 }),
916 keyboard: None,
917 consumer_control: None,
918 ..Default::default()
919 });
920 }
921 _ => panic!("InputDevice handler received an unexpected request"),
922 }
923 });
924
925 assert!(
926 is_device_type(
927 &input_device_proxy
928 .get_descriptor()
929 .await
930 .expect("Failed to get device descriptor"),
931 InputDeviceType::Touch
932 )
933 .await
934 );
935 }
936
937 #[fasync::run_singlethreaded(test)]
940 async fn touch_input_device_doesnt_exist() {
941 let input_device_proxy: fidl_input_report::InputDeviceProxy =
942 spawn_stream_handler(move |input_device_request| async move {
943 match input_device_request {
944 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
945 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
946 device_information: None,
947 mouse: None,
948 sensor: None,
949 touch: None,
950 keyboard: None,
951 consumer_control: None,
952 ..Default::default()
953 });
954 }
955 _ => panic!("InputDevice handler received an unexpected request"),
956 }
957 });
958
959 assert!(
960 !is_device_type(
961 &input_device_proxy
962 .get_descriptor()
963 .await
964 .expect("Failed to get device descriptor"),
965 InputDeviceType::Touch
966 )
967 .await
968 );
969 }
970
971 #[fasync::run_singlethreaded(test)]
974 async fn keyboard_input_device_exists() {
975 let input_device_proxy: fidl_input_report::InputDeviceProxy =
976 spawn_stream_handler(move |input_device_request| async move {
977 match input_device_request {
978 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
979 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
980 device_information: None,
981 mouse: None,
982 sensor: None,
983 touch: None,
984 keyboard: Some(fidl_input_report::KeyboardDescriptor {
985 input: Some(fidl_input_report::KeyboardInputDescriptor {
986 keys3: None,
987 ..Default::default()
988 }),
989 output: None,
990 ..Default::default()
991 }),
992 consumer_control: None,
993 ..Default::default()
994 });
995 }
996 _ => panic!("InputDevice handler received an unexpected request"),
997 }
998 });
999
1000 assert!(
1001 is_device_type(
1002 &input_device_proxy
1003 .get_descriptor()
1004 .await
1005 .expect("Failed to get device descriptor"),
1006 InputDeviceType::Keyboard
1007 )
1008 .await
1009 );
1010 }
1011
1012 #[fasync::run_singlethreaded(test)]
1015 async fn keyboard_input_device_doesnt_exist() {
1016 let input_device_proxy: fidl_input_report::InputDeviceProxy =
1017 spawn_stream_handler(move |input_device_request| async move {
1018 match input_device_request {
1019 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1020 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
1021 device_information: None,
1022 mouse: None,
1023 sensor: None,
1024 touch: None,
1025 keyboard: None,
1026 consumer_control: None,
1027 ..Default::default()
1028 });
1029 }
1030 _ => panic!("InputDevice handler received an unexpected request"),
1031 }
1032 });
1033
1034 assert!(
1035 !is_device_type(
1036 &input_device_proxy
1037 .get_descriptor()
1038 .await
1039 .expect("Failed to get device descriptor"),
1040 InputDeviceType::Keyboard
1041 )
1042 .await
1043 );
1044 }
1045
1046 #[fasync::run_singlethreaded(test)]
1048 async fn no_input_device_match() {
1049 let input_device_proxy: fidl_input_report::InputDeviceProxy =
1050 spawn_stream_handler(move |input_device_request| async move {
1051 match input_device_request {
1052 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1053 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
1054 device_information: None,
1055 mouse: Some(fidl_input_report::MouseDescriptor {
1056 input: Some(fidl_input_report::MouseInputDescriptor {
1057 movement_x: None,
1058 movement_y: None,
1059 position_x: None,
1060 position_y: None,
1061 scroll_v: None,
1062 scroll_h: None,
1063 buttons: None,
1064 ..Default::default()
1065 }),
1066 ..Default::default()
1067 }),
1068 sensor: None,
1069 touch: Some(fidl_input_report::TouchDescriptor {
1070 input: Some(fidl_input_report::TouchInputDescriptor {
1071 contacts: None,
1072 max_contacts: None,
1073 touch_type: None,
1074 buttons: None,
1075 ..Default::default()
1076 }),
1077 ..Default::default()
1078 }),
1079 keyboard: Some(fidl_input_report::KeyboardDescriptor {
1080 input: Some(fidl_input_report::KeyboardInputDescriptor {
1081 keys3: None,
1082 ..Default::default()
1083 }),
1084 output: None,
1085 ..Default::default()
1086 }),
1087 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
1088 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
1089 buttons: Some(vec![
1090 fidl_input_report::ConsumerControlButton::VolumeUp,
1091 fidl_input_report::ConsumerControlButton::VolumeDown,
1092 ]),
1093 ..Default::default()
1094 }),
1095 ..Default::default()
1096 }),
1097 ..Default::default()
1098 });
1099 }
1100 _ => panic!("InputDevice handler received an unexpected request"),
1101 }
1102 });
1103
1104 let device_descriptor =
1105 &input_device_proxy.get_descriptor().await.expect("Failed to get device descriptor");
1106 assert!(is_device_type(&device_descriptor, InputDeviceType::ConsumerControls).await);
1107 assert!(is_device_type(&device_descriptor, InputDeviceType::Mouse).await);
1108 assert!(is_device_type(&device_descriptor, InputDeviceType::Touch).await);
1109 assert!(is_device_type(&device_descriptor, InputDeviceType::Keyboard).await);
1110 }
1111
1112 #[fuchsia::test]
1113 fn unhandled_to_generic_conversion_sets_handled_flag_to_no() {
1114 assert_eq!(
1115 InputEvent::from(UnhandledInputEvent {
1116 device_event: InputDeviceEvent::Fake,
1117 device_descriptor: InputDeviceDescriptor::Fake,
1118 event_time: zx::MonotonicInstant::from_nanos(1),
1119 trace_id: None,
1120 })
1121 .handled,
1122 Handled::No
1123 );
1124 }
1125
1126 #[fuchsia::test]
1127 fn unhandled_to_generic_conversion_preserves_fields() {
1128 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1129 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1130 assert_eq!(
1131 InputEvent::from(UnhandledInputEvent {
1132 device_event: InputDeviceEvent::Fake,
1133 device_descriptor: InputDeviceDescriptor::Fake,
1134 event_time: EVENT_TIME,
1135 trace_id: expected_trace_id,
1136 }),
1137 InputEvent {
1138 device_event: InputDeviceEvent::Fake,
1139 device_descriptor: InputDeviceDescriptor::Fake,
1140 event_time: EVENT_TIME,
1141 handled: Handled::No,
1142 trace_id: expected_trace_id,
1143 },
1144 );
1145 }
1146
1147 #[fuchsia::test]
1148 fn generic_to_unhandled_conversion_fails_for_handled_events() {
1149 assert_matches!(
1150 UnhandledInputEvent::try_from(InputEvent {
1151 device_event: InputDeviceEvent::Fake,
1152 device_descriptor: InputDeviceDescriptor::Fake,
1153 event_time: zx::MonotonicInstant::from_nanos(1),
1154 handled: Handled::Yes,
1155 trace_id: None,
1156 }),
1157 Err(_)
1158 )
1159 }
1160
1161 #[fuchsia::test]
1162 fn generic_to_unhandled_conversion_preserves_fields_for_unhandled_events() {
1163 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1164 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1165 assert_eq!(
1166 UnhandledInputEvent::try_from(InputEvent {
1167 device_event: InputDeviceEvent::Fake,
1168 device_descriptor: InputDeviceDescriptor::Fake,
1169 event_time: EVENT_TIME,
1170 handled: Handled::No,
1171 trace_id: expected_trace_id,
1172 })
1173 .unwrap(),
1174 UnhandledInputEvent {
1175 device_event: InputDeviceEvent::Fake,
1176 device_descriptor: InputDeviceDescriptor::Fake,
1177 event_time: EVENT_TIME,
1178 trace_id: expected_trace_id,
1179 },
1180 )
1181 }
1182
1183 #[test_case(Handled::No; "initially not handled")]
1184 #[test_case(Handled::Yes; "initially handled")]
1185 fn into_handled_if_yields_handled_yes_on_true(initially_handled: Handled) {
1186 let event = InputEvent {
1187 device_event: InputDeviceEvent::Fake,
1188 device_descriptor: InputDeviceDescriptor::Fake,
1189 event_time: zx::MonotonicInstant::from_nanos(1),
1190 handled: initially_handled,
1191 trace_id: None,
1192 };
1193 pretty_assertions::assert_eq!(event.into_handled_if(true).handled, Handled::Yes);
1194 }
1195
1196 #[test_case(Handled::No; "initially not handled")]
1197 #[test_case(Handled::Yes; "initially handled")]
1198 fn into_handled_if_leaves_handled_unchanged_on_false(initially_handled: Handled) {
1199 let event = InputEvent {
1200 device_event: InputDeviceEvent::Fake,
1201 device_descriptor: InputDeviceDescriptor::Fake,
1202 event_time: zx::MonotonicInstant::from_nanos(1),
1203 handled: initially_handled.clone(),
1204 trace_id: None,
1205 };
1206 pretty_assertions::assert_eq!(event.into_handled_if(false).handled, initially_handled);
1207 }
1208
1209 #[test_case(Handled::No; "initially not handled")]
1210 #[test_case(Handled::Yes; "initially handled")]
1211 fn into_handled_yields_handled_yes(initially_handled: Handled) {
1212 let event = InputEvent {
1213 device_event: InputDeviceEvent::Fake,
1214 device_descriptor: InputDeviceDescriptor::Fake,
1215 event_time: zx::MonotonicInstant::from_nanos(1),
1216 handled: initially_handled,
1217 trace_id: None,
1218 };
1219 pretty_assertions::assert_eq!(event.into_handled().handled, Handled::Yes);
1220 }
1221}