1use crate::{
6 Dispatcher, Transport, consumer_controls_binding, keyboard_binding, light_sensor_binding,
7 metrics, mouse_binding, touch_binding,
8};
9use anyhow::Error;
10use async_trait::async_trait;
11use fidl_next_fuchsia_input_report::InputDevice;
12use fuchsia_inspect::health::Reporter;
13use fuchsia_inspect::{
14 ExponentialHistogramParams, HistogramProperty as _, NumericProperty, Property,
15};
16use fuchsia_trace as ftrace;
17use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
18use futures::stream::StreamExt;
19use metrics_registry::*;
20use sorted_vec_map::SortedVecSet;
21use strum_macros::{Display, EnumCount};
22
23pub use input_device_constants::InputDeviceType;
24
25#[derive(Debug, Clone, Default)]
26pub struct InputPipelineFeatureFlags {
27 pub enable_merge_touch_events: bool,
29}
30
31const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
32 floor: 0,
33 initial_step: 1,
34 step_multiplier: 10,
35 buckets: 7,
46};
47
48pub struct InputDeviceStatus {
51 now: Box<dyn Fn() -> zx::MonotonicInstant>,
54
55 _node: fuchsia_inspect::Node,
57
58 reports_received_count: fuchsia_inspect::UintProperty,
60
61 reports_filtered_count: fuchsia_inspect::UintProperty,
64
65 events_generated: fuchsia_inspect::UintProperty,
68
69 last_received_timestamp_ns: fuchsia_inspect::UintProperty,
71
72 last_generated_timestamp_ns: fuchsia_inspect::UintProperty,
74
75 pub health_node: fuchsia_inspect::health::Node,
77
78 driver_to_binding_latency_ms: fuchsia_inspect::IntExponentialHistogramProperty,
83
84 wake_lease_leak_count: fuchsia_inspect::UintProperty,
86}
87
88impl InputDeviceStatus {
89 pub fn new(device_node: fuchsia_inspect::Node) -> Self {
90 Self::new_internal(device_node, Box::new(zx::MonotonicInstant::get))
91 }
92
93 fn new_internal(
94 device_node: fuchsia_inspect::Node,
95 now: Box<dyn Fn() -> zx::MonotonicInstant>,
96 ) -> Self {
97 let mut health_node = fuchsia_inspect::health::Node::new(&device_node);
98 health_node.set_starting_up();
99
100 let reports_received_count = device_node.create_uint("reports_received_count", 0);
101 let reports_filtered_count = device_node.create_uint("reports_filtered_count", 0);
102 let events_generated = device_node.create_uint("events_generated", 0);
103 let last_received_timestamp_ns = device_node.create_uint("last_received_timestamp_ns", 0);
104 let last_generated_timestamp_ns = device_node.create_uint("last_generated_timestamp_ns", 0);
105 let driver_to_binding_latency_ms = device_node.create_int_exponential_histogram(
106 "driver_to_binding_latency_ms",
107 LATENCY_HISTOGRAM_PROPERTIES,
108 );
109 let wake_lease_leak_count = device_node.create_uint("wake_lease_leak_count", 0);
110
111 Self {
112 now,
113 _node: device_node,
114 reports_received_count,
115 reports_filtered_count,
116 events_generated,
117 last_received_timestamp_ns,
118 last_generated_timestamp_ns,
119 health_node,
120 driver_to_binding_latency_ms,
121 wake_lease_leak_count,
122 }
123 }
124
125 pub fn count_received_report_wire(
126 &self,
127 report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
128 ) {
129 self.reports_received_count.add(1);
130 match report.event_time() {
131 Some(event_time) => {
132 self.driver_to_binding_latency_ms.insert(
133 ((self.now)() - zx::MonotonicInstant::from_nanos(event_time.0)).into_millis(),
134 );
135 self.last_received_timestamp_ns.set(event_time.0.try_into().unwrap());
136 }
137 None => (),
138 }
139 }
140
141 pub fn count_filtered_report(&self) {
142 self.reports_filtered_count.add(1);
143 }
144
145 pub fn count_generated_event(&self, event: InputEvent) {
146 self.events_generated.add(1);
147 self.last_generated_timestamp_ns.set(event.event_time.into_nanos().try_into().unwrap());
148 }
149
150 pub fn count_generated_events(&self, events: &Vec<InputEvent>) {
151 self.events_generated.add(events.len() as u64);
152 if let Some(last_event) = events.last() {
153 self.last_generated_timestamp_ns
154 .set(last_event.event_time.into_nanos().try_into().unwrap());
155 }
156 }
157
158 pub fn count_wake_lease_leak(&self) {
159 self.wake_lease_leak_count.add(1);
160 }
161}
162
163#[derive(Clone, Debug, PartialEq)]
164pub enum PreviousDeviceState {
165 Keyboard {
166 pressed_keys: Vec<fidl_fuchsia_input::Key>,
167 },
168 Mouse {
169 pressed_buttons: SortedVecSet<mouse_binding::MouseButton>,
170 },
171 TouchScreen {
172 active_contacts: Vec<touch_binding::TouchContact>,
173 pressed_buttons: Vec<fidl_next_fuchsia_input_report::TouchButton>,
174 },
175 ConsumerControls {
176 pressed_buttons: Vec<fidl_fuchsia_input::ConsumerControlButton>,
177 },
178 LightSensor,
179 #[cfg(test)]
180 Fake,
181}
182
183#[derive(Clone, Debug, PartialEq)]
185pub struct InputEvent {
186 pub device_event: InputDeviceEvent,
188
189 pub device_descriptor: InputDeviceDescriptor,
192
193 pub event_time: zx::MonotonicInstant,
195
196 pub handled: Handled,
198
199 pub trace_id: Option<ftrace::Id>,
200}
201
202#[derive(Clone, Debug, PartialEq)]
208pub struct UnhandledInputEvent {
209 pub device_event: InputDeviceEvent,
211
212 pub device_descriptor: InputDeviceDescriptor,
215
216 pub event_time: zx::MonotonicInstant,
218
219 pub trace_id: Option<ftrace::Id>,
220}
221
222impl UnhandledInputEvent {
223 pub fn get_event_type(&self) -> &'static str {
225 match self.device_event {
226 InputDeviceEvent::Keyboard(_) => "keyboard_event",
227 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
228 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
229 InputDeviceEvent::Mouse(_) => "mouse_event",
230 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
231 InputDeviceEvent::Touchpad(_) => "touchpad_event",
232 #[cfg(test)]
233 InputDeviceEvent::Fake => "fake_event",
234 }
235 }
236}
237
238#[derive(Clone, Debug, PartialEq)]
247pub enum InputDeviceEvent {
248 Keyboard(keyboard_binding::KeyboardEvent),
249 LightSensor(light_sensor_binding::LightSensorEvent),
250 ConsumerControls(consumer_controls_binding::ConsumerControlsEvent),
251 Mouse(mouse_binding::MouseEvent),
252 TouchScreen(touch_binding::TouchScreenEvent),
253 Touchpad(touch_binding::TouchpadEvent),
254 #[cfg(test)]
255 Fake,
256}
257
258#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumCount, Display)]
260#[strum(serialize_all = "snake_case")]
261pub enum InputEventType {
262 Keyboard = 0,
263 LightSensor = 1,
264 ConsumerControls = 2,
265 Mouse = 3,
266 TouchScreen = 4,
267 Touchpad = 5,
268 #[cfg(test)]
269 Fake = 6,
270}
271
272impl From<&InputDeviceEvent> for InputEventType {
273 fn from(event: &InputDeviceEvent) -> Self {
274 match event {
275 InputDeviceEvent::Keyboard(_) => InputEventType::Keyboard,
276 InputDeviceEvent::LightSensor(_) => InputEventType::LightSensor,
277 InputDeviceEvent::ConsumerControls(_) => InputEventType::ConsumerControls,
278 InputDeviceEvent::Mouse(_) => InputEventType::Mouse,
279 InputDeviceEvent::TouchScreen(_) => InputEventType::TouchScreen,
280 InputDeviceEvent::Touchpad(_) => InputEventType::Touchpad,
281 #[cfg(test)]
282 InputDeviceEvent::Fake => InputEventType::Fake,
283 }
284 }
285}
286
287#[derive(Clone, Debug, PartialEq)]
297pub enum InputDeviceDescriptor {
298 Keyboard(keyboard_binding::KeyboardDeviceDescriptor),
299 LightSensor(light_sensor_binding::LightSensorDeviceDescriptor),
300 ConsumerControls(consumer_controls_binding::ConsumerControlsDeviceDescriptor),
301 Mouse(mouse_binding::MouseDeviceDescriptor),
302 TouchScreen(touch_binding::TouchScreenDeviceDescriptor),
303 Touchpad(touch_binding::TouchpadDeviceDescriptor),
304 #[cfg(test)]
305 Fake,
306}
307
308impl From<keyboard_binding::KeyboardDeviceDescriptor> for InputDeviceDescriptor {
309 fn from(b: keyboard_binding::KeyboardDeviceDescriptor) -> Self {
310 InputDeviceDescriptor::Keyboard(b)
311 }
312}
313
314impl InputDeviceDescriptor {
315 pub fn device_id(&self) -> u32 {
316 match self {
317 InputDeviceDescriptor::Keyboard(b) => b.device_id,
318 InputDeviceDescriptor::LightSensor(b) => b.device_id,
319 InputDeviceDescriptor::ConsumerControls(b) => b.device_id,
320 InputDeviceDescriptor::Mouse(b) => b.device_id,
321 InputDeviceDescriptor::TouchScreen(b) => b.device_id,
322 InputDeviceDescriptor::Touchpad(b) => b.device_id,
323 #[cfg(test)]
324 InputDeviceDescriptor::Fake => 0,
325 }
326 }
327}
328
329#[derive(Copy, Clone, Debug, PartialEq)]
331pub enum Handled {
332 Yes,
334 No,
336}
337
338#[async_trait]
347pub trait InputDeviceBinding: Send {
348 fn get_device_descriptor(&self) -> InputDeviceDescriptor;
350
351 fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>>;
353}
354
355const MAX_UNACKNOWLEDGED_REPORTS_LIMIT: u16 = 120;
372
373struct LocalReaderV2Handler<InputDeviceProcessReportsFn> {
374 client: fidl_next::Client<
375 fidl_next_fuchsia_input_report::InputReportsReaderV2,
376 fidl_next::fuchsia::zx::Channel,
377 >,
378 previous_state: Option<PreviousDeviceState>,
379 device_descriptor: InputDeviceDescriptor,
380 event_sender: UnboundedSender<Vec<InputEvent>>,
381 inspect_status: std::rc::Rc<InputDeviceStatus>,
382 metrics_logger: metrics::MetricsLogger,
383 feature_flags: InputPipelineFeatureFlags,
384 process_reports: InputDeviceProcessReportsFn,
385 ack_threshold: u64,
386 last_acknowledged_stamp: u64,
387}
388
389impl<InputDeviceProcessReportsFn>
390 fidl_next_fuchsia_input_report::InputReportsReaderV2LocalClientHandler<
391 fidl_next::fuchsia::zx::Channel,
392 > for LocalReaderV2Handler<InputDeviceProcessReportsFn>
393where
394 InputDeviceProcessReportsFn:
395 for<'de> FnMut(
396 &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
397 Option<PreviousDeviceState>,
398 &InputDeviceDescriptor,
399 &mut UnboundedSender<Vec<InputEvent>>,
400 &InputDeviceStatus,
401 &metrics::MetricsLogger,
402 &InputPipelineFeatureFlags,
403 )
404 -> (Option<PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>),
405{
406 async fn on_input_reports(
407 &mut self,
408 request: fidl_next::Request<
409 fidl_next_fuchsia_input_report::input_reports_reader_v2::OnInputReports,
410 fidl_next::fuchsia::zx::Channel,
411 >,
412 ) {
413 fuchsia_trace::duration!("input", "input-device-process-reports");
414 let payload = request.wire_payload();
415 let (prev_state, inspect_receiver) = (self.process_reports)(
418 payload.reports.as_slice(),
419 self.previous_state.take(),
420 &self.device_descriptor,
421 &mut self.event_sender,
422 &self.inspect_status,
423 &self.metrics_logger,
424 &self.feature_flags,
425 );
426 self.previous_state = prev_state;
427
428 let reports_stamp = *payload.last_report_stamp;
429 if reports_stamp.saturating_sub(self.last_acknowledged_stamp) >= self.ack_threshold {
430 if let Err(e) = self.client.acknowledge_reports(reports_stamp).await {
431 log::warn!("failed to send acknowledge_reports: {:?}", e);
432 }
433 self.last_acknowledged_stamp = reports_stamp;
434 }
435
436 if let Some(mut receiver) = inspect_receiver {
440 let inspect_status = self.inspect_status.clone();
441 let _task = Dispatcher::spawn_local(async move {
442 while let Some(event) = receiver.next().await {
443 inspect_status.count_generated_event(event);
444 }
445 });
446 }
447 }
448}
449
450pub fn initialize_report_stream<InputDeviceProcessReportsFn>(
451 device_proxy: fidl_next::Client<InputDevice, Transport>,
452 device_descriptor: InputDeviceDescriptor,
453 event_sender: UnboundedSender<Vec<InputEvent>>,
454 inspect_status: InputDeviceStatus,
455 metrics_logger: metrics::MetricsLogger,
456 feature_flags: InputPipelineFeatureFlags,
457 process_reports: InputDeviceProcessReportsFn,
458) -> crate::dispatcher::TaskHandle<()>
459where
460 InputDeviceProcessReportsFn: 'static
461 + Send
462 + for<'de> FnMut(
463 &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
464 Option<PreviousDeviceState>,
465 &InputDeviceDescriptor,
466 &mut UnboundedSender<Vec<InputEvent>>,
467 &InputDeviceStatus,
468 &metrics::MetricsLogger,
469 &InputPipelineFeatureFlags,
470 )
471 -> (Option<PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>),
472{
473 Dispatcher::spawn_local(async move {
474 let inspect_status = std::rc::Rc::new(inspect_status);
475 let (client_end, server_end) = fidl_next::fuchsia::create_channel();
476 let max_unacknowledged_reports = match device_proxy
477 .get_input_reports_reader_v2(server_end, MAX_UNACKNOWLEDGED_REPORTS_LIMIT)
478 .await
479 {
480 Ok(response) => {
481 response.max_unacknowledged_reports.min(MAX_UNACKNOWLEDGED_REPORTS_LIMIT)
482 }
483 Err(e) => {
484 metrics_logger.log_error(
485 InputPipelineErrorMetricDimensionEvent::InputDeviceGetInputReportsReaderError,
486 std::format!("error on GetInputReportsReaderV2: {:?}", e),
487 );
488 return; }
490 };
491
492 let ack_threshold = ((max_unacknowledged_reports / 2).max(1)) as u64;
493 let (_client, join_handle) = client_end.spawn_local_handler_full_on_with(
494 |client| LocalReaderV2Handler {
495 client,
496 previous_state: None,
497 device_descriptor,
498 event_sender,
499 inspect_status,
500 metrics_logger,
501 feature_flags,
502 process_reports,
503 ack_threshold,
504 last_acknowledged_stamp: 0,
505 },
506 &crate::dispatcher::LocalDriverExecutor::default(),
507 );
508
509 let _ = join_handle.await;
510 log::warn!("initialize_report_stream exited - device binding no longer works");
513 })
514}
515
516pub async fn is_device_type(
522 device_descriptor: &fidl_next_fuchsia_input_report::DeviceDescriptor,
523 device_type: InputDeviceType,
524) -> bool {
525 match device_type {
527 InputDeviceType::ConsumerControls => device_descriptor.consumer_control.is_some(),
528 InputDeviceType::Mouse => device_descriptor.mouse.is_some(),
529 InputDeviceType::Touch => device_descriptor.touch.is_some(),
530 InputDeviceType::Keyboard => device_descriptor.keyboard.is_some(),
531 InputDeviceType::LightSensor => device_descriptor.sensor.is_some(),
532 }
533}
534
535pub async fn get_device_binding(
543 device_type: InputDeviceType,
544 device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
545 device_id: u32,
546 input_event_sender: UnboundedSender<Vec<InputEvent>>,
547 device_node: fuchsia_inspect::Node,
548 feature_flags: InputPipelineFeatureFlags,
549 metrics_logger: metrics::MetricsLogger,
550 is_injected: bool,
551) -> Result<(Box<dyn InputDeviceBinding>, crate::dispatcher::TaskHandle<()>), Error> {
552 match device_type {
553 InputDeviceType::ConsumerControls => {
554 let (binding, task) = consumer_controls_binding::ConsumerControlsBinding::new(
555 device_proxy,
556 device_id,
557 input_event_sender,
558 device_node,
559 feature_flags.clone(),
560 metrics_logger,
561 is_injected,
562 )
563 .await?;
564 Ok((Box::new(binding), task))
565 }
566 InputDeviceType::Mouse => {
567 let (binding, task) = mouse_binding::MouseBinding::new(
568 device_proxy,
569 device_id,
570 input_event_sender,
571 device_node,
572 feature_flags.clone(),
573 metrics_logger,
574 )
575 .await?;
576 Ok((Box::new(binding), task))
577 }
578 InputDeviceType::Touch => {
579 let (binding, task) = touch_binding::TouchBinding::new(
580 device_proxy,
581 device_id,
582 input_event_sender,
583 device_node,
584 feature_flags.clone(),
585 metrics_logger,
586 )
587 .await?;
588 Ok((Box::new(binding), task))
589 }
590 InputDeviceType::Keyboard => {
591 let (binding, task) = keyboard_binding::KeyboardBinding::new(
592 device_proxy,
593 device_id,
594 input_event_sender,
595 device_node,
596 feature_flags.clone(),
597 metrics_logger,
598 )
599 .await?;
600 Ok((Box::new(binding), task))
601 }
602 InputDeviceType::LightSensor => {
603 let (binding, task) = light_sensor_binding::LightSensorBinding::new(
604 device_proxy,
605 device_id,
606 input_event_sender,
607 device_node,
608 feature_flags.clone(),
609 metrics_logger,
610 )
611 .await?;
612 Ok((Box::new(binding), task))
613 }
614 }
615}
616
617pub fn event_time_or_now(event_time: Option<i64>) -> zx::MonotonicInstant {
622 match event_time {
623 Some(time) => zx::MonotonicInstant::from_nanos(time),
624 None => zx::MonotonicInstant::get(),
625 }
626}
627
628impl std::convert::From<UnhandledInputEvent> for InputEvent {
629 fn from(event: UnhandledInputEvent) -> Self {
630 Self {
631 device_event: event.device_event,
632 device_descriptor: event.device_descriptor,
633 event_time: event.event_time,
634 handled: Handled::No,
635 trace_id: event.trace_id,
636 }
637 }
638}
639
640#[cfg(test)]
647impl std::convert::TryFrom<InputEvent> for UnhandledInputEvent {
648 type Error = anyhow::Error;
649 fn try_from(event: InputEvent) -> Result<UnhandledInputEvent, Self::Error> {
650 match event.handled {
651 Handled::Yes => {
652 Err(anyhow::anyhow!("Attempted to treat a handled InputEvent as unhandled"))
653 }
654 Handled::No => Ok(UnhandledInputEvent {
655 device_event: event.device_event,
656 device_descriptor: event.device_descriptor,
657 event_time: event.event_time,
658 trace_id: event.trace_id,
659 }),
660 }
661 }
662}
663
664impl InputEvent {
665 pub(crate) fn into_handled_if(self, predicate: bool) -> Self {
668 if predicate { Self { handled: Handled::Yes, ..self } } else { self }
669 }
670
671 pub(crate) fn into_handled(self) -> Self {
673 Self { handled: Handled::Yes, ..self }
674 }
675
676 pub fn into_with_event_time(self, event_time: zx::MonotonicInstant) -> Self {
678 Self { event_time, ..self }
679 }
680
681 #[cfg(test)]
683 pub fn into_with_device_descriptor(self, device_descriptor: InputDeviceDescriptor) -> Self {
684 Self { device_descriptor, ..self }
685 }
686
687 pub fn is_handled(&self) -> bool {
689 self.handled == Handled::Yes
690 }
691
692 pub fn get_event_type(&self) -> &'static str {
694 match self.device_event {
695 InputDeviceEvent::Keyboard(_) => "keyboard_event",
696 InputDeviceEvent::LightSensor(_) => "light_sensor_event",
697 InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
698 InputDeviceEvent::Mouse(_) => "mouse_event",
699 InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
700 InputDeviceEvent::Touchpad(_) => "touchpad_event",
701 #[cfg(test)]
702 InputDeviceEvent::Fake => "fake_event",
703 }
704 }
705
706 pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
707 node.record_int("event_time", self.event_time.into_nanos());
708 match &self.device_event {
709 InputDeviceEvent::LightSensor(e) => e.record_inspect(node),
710 InputDeviceEvent::ConsumerControls(e) => e.record_inspect(node),
711 InputDeviceEvent::Mouse(e) => e.record_inspect(node),
712 InputDeviceEvent::TouchScreen(e) => e.record_inspect(node),
713 InputDeviceEvent::Touchpad(e) => e.record_inspect(node),
714 InputDeviceEvent::Keyboard(_) => (),
716 #[cfg(test)] InputDeviceEvent::Fake => (),
718 }
719 }
720}
721
722#[cfg(test)]
723mod tests {
724 use super::*;
725 use crate::testing_utilities::spawn_input_stream_handler;
726 use assert_matches::assert_matches;
727 use diagnostics_assertions::AnyProperty;
728 use fidl_fuchsia_input_report as fidl_input_report;
729 use fidl_next_fuchsia_input_report::InputReport;
730 use pretty_assertions::assert_eq;
731 use std::convert::TryFrom as _;
732 use test_case::test_case;
733
734 #[test]
735 fn max_event_time() {
736 let event_time = event_time_or_now(Some(i64::MAX));
737 assert_eq!(event_time, zx::MonotonicInstant::INFINITE);
738 }
739
740 #[test]
741 fn min_event_time() {
742 let event_time = event_time_or_now(Some(i64::MIN));
743 assert_eq!(event_time, zx::MonotonicInstant::INFINITE_PAST);
744 }
745
746 #[fuchsia::test]
747 async fn input_device_status_initialized_with_correct_properties() {
748 let inspector = fuchsia_inspect::Inspector::default();
749 let input_pipeline_node = inspector.root().create_child("input_pipeline");
750 let input_devices_node = input_pipeline_node.create_child("input_devices");
751 let device_node = input_devices_node.create_child("001_keyboard");
752 let _input_device_status = InputDeviceStatus::new(device_node);
753 diagnostics_assertions::assert_data_tree!(inspector, root: {
754 input_pipeline: {
755 input_devices: {
756 "001_keyboard": {
757 reports_received_count: 0u64,
758 reports_filtered_count: 0u64,
759 events_generated: 0u64,
760 last_received_timestamp_ns: 0u64,
761 last_generated_timestamp_ns: 0u64,
762 "fuchsia.inspect.Health": {
763 status: "STARTING_UP",
764 start_timestamp_nanos: AnyProperty
767 },
768 driver_to_binding_latency_ms: diagnostics_assertions::HistogramAssertion::exponential(super::LATENCY_HISTOGRAM_PROPERTIES),
769 wake_lease_leak_count: 0u64,
770 }
771 }
772 }
773 });
774 }
775
776 #[test_case(i64::MIN; "min value")]
777 #[test_case(-1; "negative value")]
778 #[test_case(0; "zero")]
779 #[test_case(1; "positive value")]
780 #[test_case(i64::MAX; "max value")]
781 #[fuchsia::test(allow_stalls = false)]
782 async fn input_device_status_updates_latency_histogram_on_count_received_report_wire(
783 latency_nsec: i64,
784 ) {
785 let mut expected_histogram = diagnostics_assertions::HistogramAssertion::exponential(
786 super::LATENCY_HISTOGRAM_PROPERTIES,
787 );
788 let inspector = fuchsia_inspect::Inspector::default();
789 let input_device_status = InputDeviceStatus::new_internal(
790 inspector.root().clone_weak(),
791 Box::new(move || zx::MonotonicInstant::from_nanos(latency_nsec)),
792 );
793 let decoded = crate::testing_utilities::report_to_wire(InputReport {
794 event_time: Some(0),
795 ..InputReport::default()
796 });
797 input_device_status.count_received_report_wire(&decoded);
798 expected_histogram.insert_values([latency_nsec / 1000 / 1000]);
799 diagnostics_assertions::assert_data_tree!(inspector, root: contains {
800 driver_to_binding_latency_ms: expected_histogram,
801 });
802 }
803
804 #[fuchsia::test]
807 async fn consumer_controls_input_device_exists() {
808 let (input_device_proxy, _task) =
809 spawn_input_stream_handler(move |input_device_request| async move {
810 match input_device_request {
811 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
812 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
813 device_information: None,
814 mouse: None,
815 sensor: None,
816 touch: None,
817 keyboard: None,
818 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
819 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
820 buttons: Some(vec![
821 fidl_fuchsia_input::ConsumerControlButton::VolumeUp,
822 fidl_fuchsia_input::ConsumerControlButton::VolumeDown,
823 ]),
824 ..Default::default()
825 }),
826 ..Default::default()
827 }),
828 ..Default::default()
829 });
830 }
831 _ => panic!("InputDevice handler received an unexpected request"),
832 }
833 });
834
835 assert!(
836 is_device_type(
837 &input_device_proxy
838 .get_descriptor()
839 .await
840 .expect("Failed to get device descriptor")
841 .descriptor,
842 InputDeviceType::ConsumerControls
843 )
844 .await
845 );
846 }
847
848 #[fuchsia::test]
850 async fn mouse_input_device_exists() {
851 let (input_device_proxy, _task) =
852 spawn_input_stream_handler(move |input_device_request| async move {
853 match input_device_request {
854 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
855 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
856 device_information: None,
857 mouse: Some(fidl_input_report::MouseDescriptor {
858 input: Some(fidl_input_report::MouseInputDescriptor {
859 movement_x: None,
860 movement_y: None,
861 position_x: None,
862 position_y: None,
863 scroll_v: None,
864 scroll_h: None,
865 buttons: None,
866 ..Default::default()
867 }),
868 ..Default::default()
869 }),
870 sensor: None,
871 touch: None,
872 keyboard: None,
873 consumer_control: None,
874 ..Default::default()
875 });
876 }
877 _ => panic!("InputDevice handler received an unexpected request"),
878 }
879 });
880
881 assert!(
882 is_device_type(
883 &input_device_proxy
884 .get_descriptor()
885 .await
886 .expect("Failed to get device descriptor")
887 .descriptor,
888 InputDeviceType::Mouse
889 )
890 .await
891 );
892 }
893
894 #[fuchsia::test]
897 async fn mouse_input_device_doesnt_exist() {
898 let (input_device_proxy, _task) =
899 spawn_input_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: None,
907 keyboard: None,
908 consumer_control: None,
909 ..Default::default()
910 });
911 }
912 _ => panic!("InputDevice handler received an unexpected request"),
913 }
914 });
915
916 assert!(
917 !is_device_type(
918 &input_device_proxy
919 .get_descriptor()
920 .await
921 .expect("Failed to get device descriptor")
922 .descriptor,
923 InputDeviceType::Mouse
924 )
925 .await
926 );
927 }
928
929 #[fuchsia::test]
932 async fn touch_input_device_exists() {
933 let (input_device_proxy, _task) =
934 spawn_input_stream_handler(move |input_device_request| async move {
935 match input_device_request {
936 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
937 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
938 device_information: None,
939 mouse: None,
940 sensor: None,
941 touch: Some(fidl_input_report::TouchDescriptor {
942 input: Some(fidl_input_report::TouchInputDescriptor {
943 contacts: None,
944 max_contacts: None,
945 touch_type: None,
946 buttons: None,
947 ..Default::default()
948 }),
949 ..Default::default()
950 }),
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 .descriptor,
967 InputDeviceType::Touch
968 )
969 .await
970 );
971 }
972
973 #[fuchsia::test]
976 async fn touch_input_device_doesnt_exist() {
977 let (input_device_proxy, _task) =
978 spawn_input_stream_handler(move |input_device_request| async move {
979 match input_device_request {
980 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
981 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
982 device_information: None,
983 mouse: None,
984 sensor: None,
985 touch: None,
986 keyboard: None,
987 consumer_control: None,
988 ..Default::default()
989 });
990 }
991 _ => panic!("InputDevice handler received an unexpected request"),
992 }
993 });
994
995 assert!(
996 !is_device_type(
997 &input_device_proxy
998 .get_descriptor()
999 .await
1000 .expect("Failed to get device descriptor")
1001 .descriptor,
1002 InputDeviceType::Touch
1003 )
1004 .await
1005 );
1006 }
1007
1008 #[fuchsia::test]
1011 async fn keyboard_input_device_exists() {
1012 let (input_device_proxy, _task) =
1013 spawn_input_stream_handler(move |input_device_request| async move {
1014 match input_device_request {
1015 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1016 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
1017 device_information: None,
1018 mouse: None,
1019 sensor: None,
1020 touch: None,
1021 keyboard: Some(fidl_input_report::KeyboardDescriptor {
1022 input: Some(fidl_input_report::KeyboardInputDescriptor {
1023 keys3: None,
1024 ..Default::default()
1025 }),
1026 output: None,
1027 ..Default::default()
1028 }),
1029 consumer_control: None,
1030 ..Default::default()
1031 });
1032 }
1033 _ => panic!("InputDevice handler received an unexpected request"),
1034 }
1035 });
1036
1037 assert!(
1038 is_device_type(
1039 &input_device_proxy
1040 .get_descriptor()
1041 .await
1042 .expect("Failed to get device descriptor")
1043 .descriptor,
1044 InputDeviceType::Keyboard
1045 )
1046 .await
1047 );
1048 }
1049
1050 #[fuchsia::test]
1053 async fn keyboard_input_device_doesnt_exist() {
1054 let (input_device_proxy, _task) =
1055 spawn_input_stream_handler(move |input_device_request| async move {
1056 match input_device_request {
1057 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1058 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
1059 device_information: None,
1060 mouse: None,
1061 sensor: None,
1062 touch: None,
1063 keyboard: None,
1064 consumer_control: None,
1065 ..Default::default()
1066 });
1067 }
1068 _ => panic!("InputDevice handler received an unexpected request"),
1069 }
1070 });
1071
1072 assert!(
1073 !is_device_type(
1074 &input_device_proxy
1075 .get_descriptor()
1076 .await
1077 .expect("Failed to get device descriptor")
1078 .descriptor,
1079 InputDeviceType::Keyboard
1080 )
1081 .await
1082 );
1083 }
1084
1085 #[fuchsia::test]
1087 async fn no_input_device_match() {
1088 let (input_device_proxy, _task) =
1089 spawn_input_stream_handler(move |input_device_request| async move {
1090 match input_device_request {
1091 fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1092 let _ = responder.send(&fidl_input_report::DeviceDescriptor {
1093 device_information: None,
1094 mouse: Some(fidl_input_report::MouseDescriptor {
1095 input: Some(fidl_input_report::MouseInputDescriptor {
1096 movement_x: None,
1097 movement_y: None,
1098 position_x: None,
1099 position_y: None,
1100 scroll_v: None,
1101 scroll_h: None,
1102 buttons: None,
1103 ..Default::default()
1104 }),
1105 ..Default::default()
1106 }),
1107 sensor: None,
1108 touch: Some(fidl_input_report::TouchDescriptor {
1109 input: Some(fidl_input_report::TouchInputDescriptor {
1110 contacts: None,
1111 max_contacts: None,
1112 touch_type: None,
1113 buttons: None,
1114 ..Default::default()
1115 }),
1116 ..Default::default()
1117 }),
1118 keyboard: Some(fidl_input_report::KeyboardDescriptor {
1119 input: Some(fidl_input_report::KeyboardInputDescriptor {
1120 keys3: None,
1121 ..Default::default()
1122 }),
1123 output: None,
1124 ..Default::default()
1125 }),
1126 consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
1127 input: Some(fidl_input_report::ConsumerControlInputDescriptor {
1128 buttons: Some(vec![
1129 fidl_fuchsia_input::ConsumerControlButton::VolumeUp,
1130 fidl_fuchsia_input::ConsumerControlButton::VolumeDown,
1131 ]),
1132 ..Default::default()
1133 }),
1134 ..Default::default()
1135 }),
1136 ..Default::default()
1137 });
1138 }
1139 _ => panic!("InputDevice handler received an unexpected request"),
1140 }
1141 });
1142
1143 let device_descriptor = &input_device_proxy
1144 .get_descriptor()
1145 .await
1146 .expect("Failed to get device descriptor")
1147 .descriptor;
1148 assert!(is_device_type(&device_descriptor, InputDeviceType::ConsumerControls).await);
1149 assert!(is_device_type(&device_descriptor, InputDeviceType::Mouse).await);
1150 assert!(is_device_type(&device_descriptor, InputDeviceType::Touch).await);
1151 assert!(is_device_type(&device_descriptor, InputDeviceType::Keyboard).await);
1152 }
1153
1154 #[fuchsia::test]
1155 fn unhandled_to_generic_conversion_sets_handled_flag_to_no() {
1156 assert_eq!(
1157 InputEvent::from(UnhandledInputEvent {
1158 device_event: InputDeviceEvent::Fake,
1159 device_descriptor: InputDeviceDescriptor::Fake,
1160 event_time: zx::MonotonicInstant::from_nanos(1),
1161 trace_id: None,
1162 })
1163 .handled,
1164 Handled::No
1165 );
1166 }
1167
1168 #[fuchsia::test]
1169 fn unhandled_to_generic_conversion_preserves_fields() {
1170 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1171 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1172 assert_eq!(
1173 InputEvent::from(UnhandledInputEvent {
1174 device_event: InputDeviceEvent::Fake,
1175 device_descriptor: InputDeviceDescriptor::Fake,
1176 event_time: EVENT_TIME,
1177 trace_id: expected_trace_id,
1178 }),
1179 InputEvent {
1180 device_event: InputDeviceEvent::Fake,
1181 device_descriptor: InputDeviceDescriptor::Fake,
1182 event_time: EVENT_TIME,
1183 handled: Handled::No,
1184 trace_id: expected_trace_id,
1185 },
1186 );
1187 }
1188
1189 #[fuchsia::test]
1190 fn generic_to_unhandled_conversion_fails_for_handled_events() {
1191 assert_matches!(
1192 UnhandledInputEvent::try_from(InputEvent {
1193 device_event: InputDeviceEvent::Fake,
1194 device_descriptor: InputDeviceDescriptor::Fake,
1195 event_time: zx::MonotonicInstant::from_nanos(1),
1196 handled: Handled::Yes,
1197 trace_id: None,
1198 }),
1199 Err(_)
1200 )
1201 }
1202
1203 #[fuchsia::test]
1204 fn generic_to_unhandled_conversion_preserves_fields_for_unhandled_events() {
1205 const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1206 let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1207 assert_eq!(
1208 UnhandledInputEvent::try_from(InputEvent {
1209 device_event: InputDeviceEvent::Fake,
1210 device_descriptor: InputDeviceDescriptor::Fake,
1211 event_time: EVENT_TIME,
1212 handled: Handled::No,
1213 trace_id: expected_trace_id,
1214 })
1215 .unwrap(),
1216 UnhandledInputEvent {
1217 device_event: InputDeviceEvent::Fake,
1218 device_descriptor: InputDeviceDescriptor::Fake,
1219 event_time: EVENT_TIME,
1220 trace_id: expected_trace_id,
1221 },
1222 )
1223 }
1224
1225 #[test_case(Handled::No; "initially not handled")]
1226 #[test_case(Handled::Yes; "initially handled")]
1227 fn into_handled_if_yields_handled_yes_on_true(initially_handled: Handled) {
1228 let event = InputEvent {
1229 device_event: InputDeviceEvent::Fake,
1230 device_descriptor: InputDeviceDescriptor::Fake,
1231 event_time: zx::MonotonicInstant::from_nanos(1),
1232 handled: initially_handled,
1233 trace_id: None,
1234 };
1235 pretty_assertions::assert_eq!(event.into_handled_if(true).handled, Handled::Yes);
1236 }
1237
1238 #[test_case(Handled::No; "initially not handled")]
1239 #[test_case(Handled::Yes; "initially handled")]
1240 fn into_handled_if_leaves_handled_unchanged_on_false(initially_handled: Handled) {
1241 let event = InputEvent {
1242 device_event: InputDeviceEvent::Fake,
1243 device_descriptor: InputDeviceDescriptor::Fake,
1244 event_time: zx::MonotonicInstant::from_nanos(1),
1245 handled: initially_handled.clone(),
1246 trace_id: None,
1247 };
1248 pretty_assertions::assert_eq!(event.into_handled_if(false).handled, initially_handled);
1249 }
1250
1251 #[test_case(Handled::No; "initially not handled")]
1252 #[test_case(Handled::Yes; "initially handled")]
1253 fn into_handled_yields_handled_yes(initially_handled: Handled) {
1254 let event = InputEvent {
1255 device_event: InputDeviceEvent::Fake,
1256 device_descriptor: InputDeviceDescriptor::Fake,
1257 event_time: zx::MonotonicInstant::from_nanos(1),
1258 handled: initially_handled,
1259 trace_id: None,
1260 };
1261 pretty_assertions::assert_eq!(event.into_handled().handled, Handled::Yes);
1262 }
1263
1264 #[fuchsia::test]
1265 async fn initialize_report_stream_acknowledges_reports() {
1266 let (ack_sender, mut ack_receiver) = futures::channel::mpsc::unbounded::<u64>();
1267
1268 let (input_device_proxy, _task) = spawn_input_stream_handler(move |input_device_request| {
1269 let ack_sender = ack_sender.clone();
1270 async move {
1271 match input_device_request {
1272 fidl_input_report::InputDeviceRequest::GetInputReportsReaderV2 {
1273 reader,
1274 max_unacknowledged_reports_limit: _,
1275 responder,
1276 } => {
1277 responder.send(2).unwrap();
1279 let (mut request_stream, control_handle) =
1280 reader.into_stream_and_control_handle();
1281 fuchsia_async::Task::local(async move {
1282 control_handle
1284 .send_on_input_reports(
1285 vec![fidl_input_report::InputReport::default()],
1286 1,
1287 )
1288 .unwrap();
1289
1290 while let Some(Ok(req)) = request_stream.next().await {
1291 match req {
1292 fidl_input_report::InputReportsReaderV2Request::AcknowledgeReports {
1293 last_acknowledged_report_stamp,
1294 ..
1295 } => {
1296 ack_sender
1297 .unbounded_send(last_acknowledged_report_stamp)
1298 .unwrap();
1299 break;
1300 }
1301 _ => {}
1302 }
1303 }
1304 })
1305 .detach();
1306 }
1307 _ => panic!("unexpected request: {:?}", input_device_request),
1308 }
1309 }
1310 });
1311
1312 let inspector = fuchsia_inspect::Inspector::default();
1313 let device_node = inspector.root().create_child("test_device");
1314 let inspect_status = InputDeviceStatus::new(device_node);
1315 let (event_sender, _event_receiver) = futures::channel::mpsc::unbounded();
1316
1317 let _stream_task = initialize_report_stream(
1318 input_device_proxy,
1319 InputDeviceDescriptor::Fake,
1320 event_sender,
1321 inspect_status,
1322 metrics::MetricsLogger::default(),
1323 InputPipelineFeatureFlags::default(),
1324 |_reports, prev, _desc, _sender, _status, _logger, _flags| (prev, None),
1325 );
1326
1327 let acked_stamp = ack_receiver.next().await;
1328 assert_eq!(acked_stamp, Some(1));
1329 }
1330}