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