input_pipeline/
input_device.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// The path to the input-report directory.
30pub static INPUT_REPORT_PATH: &str = "/dev/class/input-report";
31
32const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
33    floor: 0,
34    initial_step: 1,
35    step_multiplier: 10,
36    // Seven buckets allows us to report
37    // *      < 0 msec (added automatically by Inspect)
38    // *      0-1 msec
39    // *     1-10 msec
40    // *   10-100 msec
41    // * 100-1000 msec
42    // *     1-10 sec
43    // *   10-100 sec
44    // * 100-1000 sec
45    // *    >1000 sec (added automatically by Inspect)
46    buckets: 7,
47};
48
49/// An [`InputDeviceStatus`] is tied to an [`InputDeviceBinding`] and provides properties
50/// detailing its Inspect status.
51pub struct InputDeviceStatus {
52    /// Function for getting the current timestamp. Enables unit testing
53    /// of the latency histogram.
54    now: Box<dyn Fn() -> zx::MonotonicInstant>,
55
56    /// A node that contains the state below.
57    _node: fuchsia_inspect::Node,
58
59    /// The total number of reports received by the device driver.
60    reports_received_count: fuchsia_inspect::UintProperty,
61
62    /// The number of reports received by the device driver that did
63    /// not get converted into InputEvents processed by InputPipeline.
64    reports_filtered_count: fuchsia_inspect::UintProperty,
65
66    /// The total number of events generated from received
67    /// InputReports that were sent to InputPipeline.
68    events_generated: fuchsia_inspect::UintProperty,
69
70    /// The event time the last received InputReport was generated.
71    last_received_timestamp_ns: fuchsia_inspect::UintProperty,
72
73    /// The event time the last InputEvent was generated.
74    last_generated_timestamp_ns: fuchsia_inspect::UintProperty,
75
76    // This node records the health status of the `InputDevice`.
77    pub health_node: fuchsia_inspect::health::Node,
78
79    /// Histogram of latency from the driver timestamp for an `InputReport` until
80    /// the time at which the report was seen by the respective binding. Reported
81    /// in milliseconds, because values less than 1 msec aren't especially
82    /// interesting.
83    driver_to_binding_latency_ms: fuchsia_inspect::IntExponentialHistogramProperty,
84}
85
86impl InputDeviceStatus {
87    pub fn new(device_node: fuchsia_inspect::Node) -> Self {
88        Self::new_internal(device_node, Box::new(zx::MonotonicInstant::get))
89    }
90
91    fn new_internal(
92        device_node: fuchsia_inspect::Node,
93        now: Box<dyn Fn() -> zx::MonotonicInstant>,
94    ) -> Self {
95        let mut health_node = fuchsia_inspect::health::Node::new(&device_node);
96        health_node.set_starting_up();
97
98        let reports_received_count = device_node.create_uint("reports_received_count", 0);
99        let reports_filtered_count = device_node.create_uint("reports_filtered_count", 0);
100        let events_generated = device_node.create_uint("events_generated", 0);
101        let last_received_timestamp_ns = device_node.create_uint("last_received_timestamp_ns", 0);
102        let last_generated_timestamp_ns = device_node.create_uint("last_generated_timestamp_ns", 0);
103        let driver_to_binding_latency_ms = device_node.create_int_exponential_histogram(
104            "driver_to_binding_latency_ms",
105            LATENCY_HISTOGRAM_PROPERTIES,
106        );
107
108        Self {
109            now,
110            _node: device_node,
111            reports_received_count,
112            reports_filtered_count,
113            events_generated,
114            last_received_timestamp_ns,
115            last_generated_timestamp_ns,
116            health_node,
117            driver_to_binding_latency_ms,
118        }
119    }
120
121    pub fn count_received_report(&self, report: &InputReport) {
122        self.reports_received_count.add(1);
123        match report.event_time {
124            Some(event_time) => {
125                self.driver_to_binding_latency_ms.insert(
126                    ((self.now)() - zx::MonotonicInstant::from_nanos(event_time)).into_millis(),
127                );
128                self.last_received_timestamp_ns.set(event_time.try_into().unwrap());
129            }
130            None => (),
131        }
132    }
133
134    pub fn count_filtered_report(&self) {
135        self.reports_filtered_count.add(1);
136    }
137
138    pub fn count_generated_event(&self, event: InputEvent) {
139        self.events_generated.add(1);
140        self.last_generated_timestamp_ns.set(event.event_time.into_nanos().try_into().unwrap());
141    }
142}
143
144/// An [`InputEvent`] holds information about an input event and the device that produced the event.
145#[derive(Clone, Debug, PartialEq)]
146pub struct InputEvent {
147    /// The `device_event` contains the device-specific input event information.
148    pub device_event: InputDeviceEvent,
149
150    /// The `device_descriptor` contains static information about the device that generated the
151    /// input event.
152    pub device_descriptor: InputDeviceDescriptor,
153
154    /// The time in nanoseconds when the event was first recorded.
155    pub event_time: zx::MonotonicInstant,
156
157    /// The handled state of the event.
158    pub handled: Handled,
159
160    pub trace_id: Option<ftrace::Id>,
161}
162
163/// An [`UnhandledInputEvent`] is like an [`InputEvent`], except that the data represents an
164/// event that has not been handled.
165/// * Event producers must not use this type to carry data for an event that was already
166///   handled.
167/// * Event consumers should assume that the event has not been handled.
168#[derive(Clone, Debug, PartialEq)]
169pub struct UnhandledInputEvent {
170    /// The `device_event` contains the device-specific input event information.
171    pub device_event: InputDeviceEvent,
172
173    /// The `device_descriptor` contains static information about the device that generated the
174    /// input event.
175    pub device_descriptor: InputDeviceDescriptor,
176
177    /// The time in nanoseconds when the event was first recorded.
178    pub event_time: zx::MonotonicInstant,
179
180    pub trace_id: Option<ftrace::Id>,
181}
182
183impl UnhandledInputEvent {
184    // Returns event type as string.
185    pub fn get_event_type(&self) -> &'static str {
186        match self.device_event {
187            InputDeviceEvent::Keyboard(_) => "keyboard_event",
188            InputDeviceEvent::LightSensor(_) => "light_sensor_event",
189            InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
190            InputDeviceEvent::Mouse(_) => "mouse_event",
191            InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
192            InputDeviceEvent::Touchpad(_) => "touchpad_event",
193            #[cfg(test)]
194            InputDeviceEvent::Fake => "fake_event",
195        }
196    }
197}
198
199/// An [`InputDeviceEvent`] represents an input event from an input device.
200///
201/// [`InputDeviceEvent`]s contain more context than the raw [`InputReport`] they are parsed from.
202/// For example, [`KeyboardEvent`] contains all the pressed keys, as well as the key's
203/// phase (pressed, released, etc.).
204///
205/// Each [`InputDeviceBinding`] generates the type of [`InputDeviceEvent`]s that are appropriate
206/// for their device.
207#[derive(Clone, Debug, PartialEq)]
208pub enum InputDeviceEvent {
209    Keyboard(keyboard_binding::KeyboardEvent),
210    LightSensor(light_sensor_binding::LightSensorEvent),
211    ConsumerControls(consumer_controls_binding::ConsumerControlsEvent),
212    Mouse(mouse_binding::MouseEvent),
213    TouchScreen(touch_binding::TouchScreenEvent),
214    Touchpad(touch_binding::TouchpadEvent),
215    #[cfg(test)]
216    Fake,
217}
218
219/// An [`InputEventType`] represents the type of an input event.
220#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
221pub enum InputEventType {
222    Keyboard,
223    LightSensor,
224    ConsumerControls,
225    Mouse,
226    TouchScreen,
227    Touchpad,
228    #[cfg(test)]
229    Fake,
230}
231
232impl From<&InputDeviceEvent> for InputEventType {
233    fn from(event: &InputDeviceEvent) -> Self {
234        match event {
235            InputDeviceEvent::Keyboard(_) => InputEventType::Keyboard,
236            InputDeviceEvent::LightSensor(_) => InputEventType::LightSensor,
237            InputDeviceEvent::ConsumerControls(_) => InputEventType::ConsumerControls,
238            InputDeviceEvent::Mouse(_) => InputEventType::Mouse,
239            InputDeviceEvent::TouchScreen(_) => InputEventType::TouchScreen,
240            InputDeviceEvent::Touchpad(_) => InputEventType::Touchpad,
241            #[cfg(test)]
242            InputDeviceEvent::Fake => InputEventType::Fake,
243        }
244    }
245}
246
247/// An [`InputDescriptor`] describes the ranges of values a particular input device can generate.
248///
249/// For example, a [`InputDescriptor::Keyboard`] contains the keys available on the keyboard,
250/// and a [`InputDescriptor::Touch`] contains the maximum number of touch contacts and the
251/// range of x- and y-values each contact can take on.
252///
253/// The descriptor is sent alongside [`InputDeviceEvent`]s so clients can, for example, convert a
254/// touch coordinate to a display coordinate. The descriptor is not expected to change for the
255/// lifetime of a device binding.
256#[derive(Clone, Debug, PartialEq)]
257pub enum InputDeviceDescriptor {
258    Keyboard(keyboard_binding::KeyboardDeviceDescriptor),
259    LightSensor(light_sensor_binding::LightSensorDeviceDescriptor),
260    ConsumerControls(consumer_controls_binding::ConsumerControlsDeviceDescriptor),
261    Mouse(mouse_binding::MouseDeviceDescriptor),
262    TouchScreen(touch_binding::TouchScreenDeviceDescriptor),
263    Touchpad(touch_binding::TouchpadDeviceDescriptor),
264    #[cfg(test)]
265    Fake,
266}
267
268impl From<keyboard_binding::KeyboardDeviceDescriptor> for InputDeviceDescriptor {
269    fn from(b: keyboard_binding::KeyboardDeviceDescriptor) -> Self {
270        InputDeviceDescriptor::Keyboard(b)
271    }
272}
273
274// Whether the event is consumed by an [`InputHandler`].
275#[derive(Copy, Clone, Debug, PartialEq)]
276pub enum Handled {
277    // The event has been handled.
278    Yes,
279    // The event has not been handled.
280    No,
281}
282
283/// An [`InputDeviceBinding`] represents a binding to an input device (e.g., a mouse).
284///
285/// [`InputDeviceBinding`]s expose information about the bound device. For example, a
286/// [`MouseBinding`] exposes the ranges of possible x and y values the device can generate.
287///
288/// An [`InputPipeline`] manages [`InputDeviceBinding`]s and holds the receiving end of a channel
289/// that an [`InputDeviceBinding`]s send [`InputEvent`]s over.
290/// ```
291#[async_trait]
292pub trait InputDeviceBinding: Send {
293    /// Returns information about the input device.
294    fn get_device_descriptor(&self) -> InputDeviceDescriptor;
295
296    /// Returns the input event stream's sender.
297    fn input_event_sender(&self) -> UnboundedSender<InputEvent>;
298}
299
300/// Initializes the input report stream for the device bound to `device_proxy`.
301///
302/// Spawns a future which awaits input reports from the device and forwards them to
303/// clients via `event_sender`.
304///
305/// # Parameters
306/// - `device_proxy`: The device proxy which is used to get input reports.
307/// - `device_descriptor`: The descriptor of the device bound to `device_proxy`.
308/// - `event_sender`: The channel to send InputEvents to.
309/// - `metrics_logger`: The metrics logger.
310/// - `process_reports`: A function that generates InputEvent(s) from an InputReport and the
311///                      InputReport that precedes it. Each type of input device defines how it
312///                      processes InputReports.
313///
314pub fn initialize_report_stream<InputDeviceProcessReportsFn>(
315    device_proxy: fidl_input_report::InputDeviceProxy,
316    device_descriptor: InputDeviceDescriptor,
317    mut event_sender: UnboundedSender<InputEvent>,
318    inspect_status: InputDeviceStatus,
319    metrics_logger: metrics::MetricsLogger,
320    mut process_reports: InputDeviceProcessReportsFn,
321) where
322    InputDeviceProcessReportsFn: 'static
323        + Send
324        + FnMut(
325            Vec<InputReport>,
326            Option<InputReport>,
327            &InputDeviceDescriptor,
328            &mut UnboundedSender<InputEvent>,
329            &InputDeviceStatus,
330            &metrics::MetricsLogger,
331        ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>),
332{
333    fasync::Task::local(async move {
334        let mut previous_report: Option<InputReport> = None;
335        let (report_reader, server_end) = fidl::endpoints::create_proxy();
336        let result = device_proxy.get_input_reports_reader(server_end);
337        if result.is_err() {
338            metrics_logger.log_error(
339                InputPipelineErrorMetricDimensionEvent::InputDeviceGetInputReportsReaderError,
340                std::format!("error on GetInputReportsReader: {:?}", &result),
341            );
342            return; // TODO(https://fxbug.dev/42131965): signal error
343        }
344        let mut report_stream = HangingGetStream::new(
345            report_reader,
346            fidl_input_report::InputReportsReaderProxy::read_input_reports,
347        );
348        loop {
349            match report_stream.next().await {
350                Some(Ok(Ok(input_reports))) => {
351                    fuchsia_trace::duration!("input", "input-device-process-reports");
352                    let (prev_report, inspect_receiver) = process_reports(
353                        input_reports,
354                        previous_report,
355                        &device_descriptor,
356                        &mut event_sender,
357                        &inspect_status,
358                        &metrics_logger,
359                    );
360                    previous_report = prev_report;
361
362                    // If a report generates multiple events asynchronously, we send them over a mpsc channel
363                    // to inspect_receiver. We update the event count on inspect_status here since we cannot
364                    // pass a reference to inspect_status to an async task in process_reports().
365                    match inspect_receiver {
366                        Some(mut receiver) => {
367                            while let Some(event) = receiver.next().await {
368                                inspect_status.count_generated_event(event);
369                            }
370                        }
371                        None => (),
372                    };
373                }
374                Some(Ok(Err(_service_error))) => break,
375                Some(Err(_fidl_error)) => break,
376                None => break,
377            }
378        }
379        // TODO(https://fxbug.dev/42131965): Add signaling for when this loop exits, since it means the device
380        // binding is no longer functional.
381        log::warn!("initialize_report_stream exited - device binding no longer works");
382    })
383    .detach();
384}
385
386/// Returns true if the device type of `input_device` matches `device_type`.
387///
388/// # Parameters
389/// - `input_device`: The InputDevice to check the type of.
390/// - `device_type`: The type of the device to compare to.
391pub async fn is_device_type(
392    device_descriptor: &fidl_input_report::DeviceDescriptor,
393    device_type: InputDeviceType,
394) -> bool {
395    // Return if the device type matches the desired `device_type`.
396    match device_type {
397        InputDeviceType::ConsumerControls => device_descriptor.consumer_control.is_some(),
398        InputDeviceType::Mouse => device_descriptor.mouse.is_some(),
399        InputDeviceType::Touch => device_descriptor.touch.is_some(),
400        InputDeviceType::Keyboard => device_descriptor.keyboard.is_some(),
401        InputDeviceType::LightSensor => device_descriptor.sensor.is_some(),
402    }
403}
404
405/// Returns a new [`InputDeviceBinding`] of the given device type.
406///
407/// # Parameters
408/// - `device_type`: The type of the input device.
409/// - `device_proxy`: The device proxy which is used to get input reports.
410/// - `device_id`: The id of the connected input device.
411/// - `input_event_sender`: The channel to send generated InputEvents to.
412pub async fn get_device_binding(
413    device_type: InputDeviceType,
414    device_proxy: fidl_input_report::InputDeviceProxy,
415    device_id: u32,
416    input_event_sender: UnboundedSender<InputEvent>,
417    device_node: fuchsia_inspect::Node,
418    metrics_logger: metrics::MetricsLogger,
419) -> Result<Box<dyn InputDeviceBinding>, Error> {
420    match device_type {
421        InputDeviceType::ConsumerControls => {
422            let binding = consumer_controls_binding::ConsumerControlsBinding::new(
423                device_proxy,
424                device_id,
425                input_event_sender,
426                device_node,
427                metrics_logger,
428            )
429            .await?;
430            Ok(Box::new(binding))
431        }
432        InputDeviceType::Mouse => {
433            let binding = mouse_binding::MouseBinding::new(
434                device_proxy,
435                device_id,
436                input_event_sender,
437                device_node,
438                metrics_logger,
439            )
440            .await?;
441            Ok(Box::new(binding))
442        }
443        InputDeviceType::Touch => {
444            let binding = touch_binding::TouchBinding::new(
445                device_proxy,
446                device_id,
447                input_event_sender,
448                device_node,
449                metrics_logger,
450            )
451            .await?;
452            Ok(Box::new(binding))
453        }
454        InputDeviceType::Keyboard => {
455            let binding = keyboard_binding::KeyboardBinding::new(
456                device_proxy,
457                device_id,
458                input_event_sender,
459                device_node,
460                metrics_logger,
461            )
462            .await?;
463            Ok(Box::new(binding))
464        }
465        InputDeviceType::LightSensor => {
466            let binding = light_sensor_binding::LightSensorBinding::new(
467                device_proxy,
468                device_id,
469                input_event_sender,
470                device_node,
471                metrics_logger,
472            )
473            .await?;
474            Ok(Box::new(binding))
475        }
476    }
477}
478
479/// Returns a proxy to the InputDevice in `entry_path` if it exists.
480///
481/// # Parameters
482/// - `dir_proxy`: The directory containing InputDevice connections.
483/// - `entry_path`: The directory entry that contains an InputDevice.
484///
485/// # Errors
486/// If there is an error connecting to the InputDevice in `entry_path`.
487pub fn get_device_from_dir_entry_path(
488    dir_proxy: &fio::DirectoryProxy,
489    entry_path: &PathBuf,
490) -> Result<fidl_input_report::InputDeviceProxy, Error> {
491    let input_device_path = entry_path.to_str();
492    if input_device_path.is_none() {
493        return Err(format_err!("Failed to get entry path as a string."));
494    }
495
496    let (input_device, server) = fidl::endpoints::create_proxy::<InputDeviceMarker>();
497    fdio::service_connect_at(
498        dir_proxy.as_channel().as_ref(),
499        input_device_path.unwrap(),
500        server.into_channel(),
501    )
502    .expect("Failed to connect to InputDevice.");
503    Ok(input_device)
504}
505
506/// Returns the event time if it exists, otherwise returns the current time.
507///
508/// # Parameters
509/// - `event_time`: The event time from an InputReport.
510pub fn event_time_or_now(event_time: Option<i64>) -> zx::MonotonicInstant {
511    match event_time {
512        Some(time) => zx::MonotonicInstant::from_nanos(time),
513        None => zx::MonotonicInstant::get(),
514    }
515}
516
517impl std::convert::From<UnhandledInputEvent> for InputEvent {
518    fn from(event: UnhandledInputEvent) -> Self {
519        Self {
520            device_event: event.device_event,
521            device_descriptor: event.device_descriptor,
522            event_time: event.event_time,
523            handled: Handled::No,
524            trace_id: event.trace_id,
525        }
526    }
527}
528
529// Fallible conversion from an InputEvent to an UnhandledInputEvent.
530//
531// Useful to adapt various functions in the [`testing_utilities`] module
532// to work with tests for [`UnhandledInputHandler`]s.
533//
534// Production code however, should probably just match on the [`InputEvent`].
535#[cfg(test)]
536impl std::convert::TryFrom<InputEvent> for UnhandledInputEvent {
537    type Error = anyhow::Error;
538    fn try_from(event: InputEvent) -> Result<UnhandledInputEvent, Self::Error> {
539        match event.handled {
540            Handled::Yes => {
541                Err(format_err!("Attempted to treat a handled InputEvent as unhandled"))
542            }
543            Handled::No => Ok(UnhandledInputEvent {
544                device_event: event.device_event,
545                device_descriptor: event.device_descriptor,
546                event_time: event.event_time,
547                trace_id: event.trace_id,
548            }),
549        }
550    }
551}
552
553impl InputEvent {
554    pub fn clone_with_wake_lease(&self) -> Self {
555        let device_event = match &self.device_event {
556            InputDeviceEvent::ConsumerControls(event) => {
557                InputDeviceEvent::ConsumerControls(event.clone_with_wake_lease())
558            }
559            InputDeviceEvent::Mouse(event) => {
560                InputDeviceEvent::Mouse(event.clone_with_wake_lease())
561            }
562            InputDeviceEvent::TouchScreen(event) => {
563                InputDeviceEvent::TouchScreen(event.clone_with_wake_lease())
564            }
565            _ => self.device_event.clone(),
566        };
567        Self {
568            device_event,
569            device_descriptor: self.device_descriptor.clone(),
570            event_time: self.event_time,
571            handled: self.handled,
572            trace_id: self.trace_id,
573        }
574    }
575
576    /// Marks the event as handled, if `predicate` is `true`.
577    /// Otherwise, leaves the event unchanged.
578    pub(crate) fn into_handled_if(self, predicate: bool) -> Self {
579        if predicate { Self { handled: Handled::Yes, ..self } } else { self }
580    }
581
582    /// Marks the event as handled.
583    pub(crate) fn into_handled(self) -> Self {
584        Self { handled: Handled::Yes, ..self }
585    }
586
587    /// Returns the same event, with modified event time.
588    pub fn into_with_event_time(self, event_time: zx::MonotonicInstant) -> Self {
589        Self { event_time, ..self }
590    }
591
592    /// Returns the same event, with modified device descriptor.
593    #[cfg(test)]
594    pub fn into_with_device_descriptor(self, device_descriptor: InputDeviceDescriptor) -> Self {
595        Self { device_descriptor, ..self }
596    }
597
598    /// Returns true if this event is marked as handled.
599    pub fn is_handled(&self) -> bool {
600        self.handled == Handled::Yes
601    }
602
603    // Returns event type as string.
604    pub fn get_event_type(&self) -> &'static str {
605        match self.device_event {
606            InputDeviceEvent::Keyboard(_) => "keyboard_event",
607            InputDeviceEvent::LightSensor(_) => "light_sensor_event",
608            InputDeviceEvent::ConsumerControls(_) => "consumer_controls_event",
609            InputDeviceEvent::Mouse(_) => "mouse_event",
610            InputDeviceEvent::TouchScreen(_) => "touch_screen_event",
611            InputDeviceEvent::Touchpad(_) => "touchpad_event",
612            #[cfg(test)]
613            InputDeviceEvent::Fake => "fake_event",
614        }
615    }
616
617    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
618        node.record_int("event_time", self.event_time.into_nanos());
619        match &self.device_event {
620            InputDeviceEvent::LightSensor(e) => e.record_inspect(node),
621            InputDeviceEvent::ConsumerControls(e) => e.record_inspect(node),
622            InputDeviceEvent::Mouse(e) => e.record_inspect(node),
623            InputDeviceEvent::TouchScreen(e) => e.record_inspect(node),
624            InputDeviceEvent::Touchpad(e) => e.record_inspect(node),
625            // No-op for KeyboardEvent, since we don't want to potentially record sensitive information to Inspect.
626            InputDeviceEvent::Keyboard(_) => (),
627            #[cfg(test)] // No-op for Fake InputDeviceEvent.
628            InputDeviceEvent::Fake => (),
629        }
630    }
631}
632
633#[cfg(test)]
634mod tests {
635    use super::*;
636    use assert_matches::assert_matches;
637    use diagnostics_assertions::AnyProperty;
638    use fidl_test_util::spawn_stream_handler;
639
640    use pretty_assertions::assert_eq;
641    use std::convert::TryFrom as _;
642    use test_case::test_case;
643
644    #[test]
645    fn max_event_time() {
646        let event_time = event_time_or_now(Some(i64::MAX));
647        assert_eq!(event_time, zx::MonotonicInstant::INFINITE);
648    }
649
650    #[test]
651    fn min_event_time() {
652        let event_time = event_time_or_now(Some(std::i64::MIN));
653        assert_eq!(event_time, zx::MonotonicInstant::INFINITE_PAST);
654    }
655
656    #[fasync::run_singlethreaded(test)]
657    async fn input_device_status_initialized_with_correct_properties() {
658        let inspector = fuchsia_inspect::Inspector::default();
659        let input_pipeline_node = inspector.root().create_child("input_pipeline");
660        let input_devices_node = input_pipeline_node.create_child("input_devices");
661        let device_node = input_devices_node.create_child("001_keyboard");
662        let _input_device_status = InputDeviceStatus::new(device_node);
663        diagnostics_assertions::assert_data_tree!(inspector, root: {
664            input_pipeline: {
665                input_devices: {
666                    "001_keyboard": {
667                        reports_received_count: 0u64,
668                        reports_filtered_count: 0u64,
669                        events_generated: 0u64,
670                        last_received_timestamp_ns: 0u64,
671                        last_generated_timestamp_ns: 0u64,
672                        "fuchsia.inspect.Health": {
673                            status: "STARTING_UP",
674                            // Timestamp value is unpredictable and not relevant in this context,
675                            // so we only assert that the property is present.
676                            start_timestamp_nanos: AnyProperty
677                        },
678                        driver_to_binding_latency_ms: diagnostics_assertions::HistogramAssertion::exponential(super::LATENCY_HISTOGRAM_PROPERTIES),
679                    }
680                }
681            }
682        });
683    }
684
685    #[test_case(i64::MIN; "min value")]
686    #[test_case(-1; "negative value")]
687    #[test_case(0; "zero")]
688    #[test_case(1; "positive value")]
689    #[test_case(i64::MAX; "max value")]
690    #[fuchsia::test(allow_stalls = false)]
691    async fn input_device_status_updates_latency_histogram_on_count_received_report(
692        latency_nsec: i64,
693    ) {
694        let mut expected_histogram = diagnostics_assertions::HistogramAssertion::exponential(
695            super::LATENCY_HISTOGRAM_PROPERTIES,
696        );
697        let inspector = fuchsia_inspect::Inspector::default();
698        let input_device_status = InputDeviceStatus::new_internal(
699            inspector.root().clone_weak(),
700            Box::new(move || zx::MonotonicInstant::from_nanos(latency_nsec)),
701        );
702        input_device_status
703            .count_received_report(&InputReport { event_time: Some(0), ..InputReport::default() });
704        expected_histogram.insert_values([latency_nsec / 1000 / 1000]);
705        diagnostics_assertions::assert_data_tree!(inspector, root: contains {
706            driver_to_binding_latency_ms: expected_histogram,
707        });
708    }
709
710    // Tests that is_device_type() returns true for InputDeviceType::ConsumerControls when a
711    // consumer controls device exists.
712    #[fasync::run_singlethreaded(test)]
713    async fn consumer_controls_input_device_exists() {
714        let input_device_proxy: fidl_input_report::InputDeviceProxy =
715            spawn_stream_handler(move |input_device_request| async move {
716                match input_device_request {
717                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
718                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
719                            device_information: None,
720                            mouse: None,
721                            sensor: None,
722                            touch: None,
723                            keyboard: None,
724                            consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
725                                input: Some(fidl_input_report::ConsumerControlInputDescriptor {
726                                    buttons: Some(vec![
727                                        fidl_input_report::ConsumerControlButton::VolumeUp,
728                                        fidl_input_report::ConsumerControlButton::VolumeDown,
729                                    ]),
730                                    ..Default::default()
731                                }),
732                                ..Default::default()
733                            }),
734                            ..Default::default()
735                        });
736                    }
737                    _ => panic!("InputDevice handler received an unexpected request"),
738                }
739            });
740
741        assert!(
742            is_device_type(
743                &input_device_proxy
744                    .get_descriptor()
745                    .await
746                    .expect("Failed to get device descriptor"),
747                InputDeviceType::ConsumerControls
748            )
749            .await
750        );
751    }
752
753    // Tests that is_device_type() returns true for InputDeviceType::Mouse when a mouse exists.
754    #[fasync::run_singlethreaded(test)]
755    async fn mouse_input_device_exists() {
756        let input_device_proxy: fidl_input_report::InputDeviceProxy =
757            spawn_stream_handler(move |input_device_request| async move {
758                match input_device_request {
759                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
760                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
761                            device_information: None,
762                            mouse: Some(fidl_input_report::MouseDescriptor {
763                                input: Some(fidl_input_report::MouseInputDescriptor {
764                                    movement_x: None,
765                                    movement_y: None,
766                                    position_x: None,
767                                    position_y: None,
768                                    scroll_v: None,
769                                    scroll_h: None,
770                                    buttons: None,
771                                    ..Default::default()
772                                }),
773                                ..Default::default()
774                            }),
775                            sensor: None,
776                            touch: None,
777                            keyboard: None,
778                            consumer_control: None,
779                            ..Default::default()
780                        });
781                    }
782                    _ => panic!("InputDevice handler received an unexpected request"),
783                }
784            });
785
786        assert!(
787            is_device_type(
788                &input_device_proxy
789                    .get_descriptor()
790                    .await
791                    .expect("Failed to get device descriptor"),
792                InputDeviceType::Mouse
793            )
794            .await
795        );
796    }
797
798    // Tests that is_device_type() returns true for InputDeviceType::Mouse when a mouse doesn't
799    // exist.
800    #[fasync::run_singlethreaded(test)]
801    async fn mouse_input_device_doesnt_exist() {
802        let input_device_proxy: fidl_input_report::InputDeviceProxy =
803            spawn_stream_handler(move |input_device_request| async move {
804                match input_device_request {
805                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
806                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
807                            device_information: None,
808                            mouse: None,
809                            sensor: None,
810                            touch: None,
811                            keyboard: None,
812                            consumer_control: None,
813                            ..Default::default()
814                        });
815                    }
816                    _ => panic!("InputDevice handler received an unexpected request"),
817                }
818            });
819
820        assert!(
821            !is_device_type(
822                &input_device_proxy
823                    .get_descriptor()
824                    .await
825                    .expect("Failed to get device descriptor"),
826                InputDeviceType::Mouse
827            )
828            .await
829        );
830    }
831
832    // Tests that is_device_type() returns true for InputDeviceType::Touch when a touchscreen
833    // exists.
834    #[fasync::run_singlethreaded(test)]
835    async fn touch_input_device_exists() {
836        let input_device_proxy: fidl_input_report::InputDeviceProxy =
837            spawn_stream_handler(move |input_device_request| async move {
838                match input_device_request {
839                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
840                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
841                            device_information: None,
842                            mouse: None,
843                            sensor: None,
844                            touch: Some(fidl_input_report::TouchDescriptor {
845                                input: Some(fidl_input_report::TouchInputDescriptor {
846                                    contacts: None,
847                                    max_contacts: None,
848                                    touch_type: None,
849                                    buttons: None,
850                                    ..Default::default()
851                                }),
852                                ..Default::default()
853                            }),
854                            keyboard: None,
855                            consumer_control: None,
856                            ..Default::default()
857                        });
858                    }
859                    _ => panic!("InputDevice handler received an unexpected request"),
860                }
861            });
862
863        assert!(
864            is_device_type(
865                &input_device_proxy
866                    .get_descriptor()
867                    .await
868                    .expect("Failed to get device descriptor"),
869                InputDeviceType::Touch
870            )
871            .await
872        );
873    }
874
875    // Tests that is_device_type() returns true for InputDeviceType::Touch when a touchscreen
876    // exists.
877    #[fasync::run_singlethreaded(test)]
878    async fn touch_input_device_doesnt_exist() {
879        let input_device_proxy: fidl_input_report::InputDeviceProxy =
880            spawn_stream_handler(move |input_device_request| async move {
881                match input_device_request {
882                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
883                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
884                            device_information: None,
885                            mouse: None,
886                            sensor: None,
887                            touch: None,
888                            keyboard: None,
889                            consumer_control: None,
890                            ..Default::default()
891                        });
892                    }
893                    _ => panic!("InputDevice handler received an unexpected request"),
894                }
895            });
896
897        assert!(
898            !is_device_type(
899                &input_device_proxy
900                    .get_descriptor()
901                    .await
902                    .expect("Failed to get device descriptor"),
903                InputDeviceType::Touch
904            )
905            .await
906        );
907    }
908
909    // Tests that is_device_type() returns true for InputDeviceType::Keyboard when a keyboard
910    // exists.
911    #[fasync::run_singlethreaded(test)]
912    async fn keyboard_input_device_exists() {
913        let input_device_proxy: fidl_input_report::InputDeviceProxy =
914            spawn_stream_handler(move |input_device_request| async move {
915                match input_device_request {
916                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
917                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
918                            device_information: None,
919                            mouse: None,
920                            sensor: None,
921                            touch: None,
922                            keyboard: Some(fidl_input_report::KeyboardDescriptor {
923                                input: Some(fidl_input_report::KeyboardInputDescriptor {
924                                    keys3: None,
925                                    ..Default::default()
926                                }),
927                                output: None,
928                                ..Default::default()
929                            }),
930                            consumer_control: None,
931                            ..Default::default()
932                        });
933                    }
934                    _ => panic!("InputDevice handler received an unexpected request"),
935                }
936            });
937
938        assert!(
939            is_device_type(
940                &input_device_proxy
941                    .get_descriptor()
942                    .await
943                    .expect("Failed to get device descriptor"),
944                InputDeviceType::Keyboard
945            )
946            .await
947        );
948    }
949
950    // Tests that is_device_type() returns true for InputDeviceType::Keyboard when a keyboard
951    // exists.
952    #[fasync::run_singlethreaded(test)]
953    async fn keyboard_input_device_doesnt_exist() {
954        let input_device_proxy: fidl_input_report::InputDeviceProxy =
955            spawn_stream_handler(move |input_device_request| async move {
956                match input_device_request {
957                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
958                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
959                            device_information: None,
960                            mouse: None,
961                            sensor: None,
962                            touch: None,
963                            keyboard: None,
964                            consumer_control: None,
965                            ..Default::default()
966                        });
967                    }
968                    _ => panic!("InputDevice handler received an unexpected request"),
969                }
970            });
971
972        assert!(
973            !is_device_type(
974                &input_device_proxy
975                    .get_descriptor()
976                    .await
977                    .expect("Failed to get device descriptor"),
978                InputDeviceType::Keyboard
979            )
980            .await
981        );
982    }
983
984    // Tests that is_device_type() returns true for every input device type that exists.
985    #[fasync::run_singlethreaded(test)]
986    async fn no_input_device_match() {
987        let input_device_proxy: fidl_input_report::InputDeviceProxy =
988            spawn_stream_handler(move |input_device_request| async move {
989                match input_device_request {
990                    fidl_input_report::InputDeviceRequest::GetDescriptor { responder } => {
991                        let _ = responder.send(&fidl_input_report::DeviceDescriptor {
992                            device_information: None,
993                            mouse: Some(fidl_input_report::MouseDescriptor {
994                                input: Some(fidl_input_report::MouseInputDescriptor {
995                                    movement_x: None,
996                                    movement_y: None,
997                                    position_x: None,
998                                    position_y: None,
999                                    scroll_v: None,
1000                                    scroll_h: None,
1001                                    buttons: None,
1002                                    ..Default::default()
1003                                }),
1004                                ..Default::default()
1005                            }),
1006                            sensor: None,
1007                            touch: Some(fidl_input_report::TouchDescriptor {
1008                                input: Some(fidl_input_report::TouchInputDescriptor {
1009                                    contacts: None,
1010                                    max_contacts: None,
1011                                    touch_type: None,
1012                                    buttons: None,
1013                                    ..Default::default()
1014                                }),
1015                                ..Default::default()
1016                            }),
1017                            keyboard: Some(fidl_input_report::KeyboardDescriptor {
1018                                input: Some(fidl_input_report::KeyboardInputDescriptor {
1019                                    keys3: None,
1020                                    ..Default::default()
1021                                }),
1022                                output: None,
1023                                ..Default::default()
1024                            }),
1025                            consumer_control: Some(fidl_input_report::ConsumerControlDescriptor {
1026                                input: Some(fidl_input_report::ConsumerControlInputDescriptor {
1027                                    buttons: Some(vec![
1028                                        fidl_input_report::ConsumerControlButton::VolumeUp,
1029                                        fidl_input_report::ConsumerControlButton::VolumeDown,
1030                                    ]),
1031                                    ..Default::default()
1032                                }),
1033                                ..Default::default()
1034                            }),
1035                            ..Default::default()
1036                        });
1037                    }
1038                    _ => panic!("InputDevice handler received an unexpected request"),
1039                }
1040            });
1041
1042        let device_descriptor =
1043            &input_device_proxy.get_descriptor().await.expect("Failed to get device descriptor");
1044        assert!(is_device_type(&device_descriptor, InputDeviceType::ConsumerControls).await);
1045        assert!(is_device_type(&device_descriptor, InputDeviceType::Mouse).await);
1046        assert!(is_device_type(&device_descriptor, InputDeviceType::Touch).await);
1047        assert!(is_device_type(&device_descriptor, InputDeviceType::Keyboard).await);
1048    }
1049
1050    #[fuchsia::test]
1051    fn unhandled_to_generic_conversion_sets_handled_flag_to_no() {
1052        assert_eq!(
1053            InputEvent::from(UnhandledInputEvent {
1054                device_event: InputDeviceEvent::Fake,
1055                device_descriptor: InputDeviceDescriptor::Fake,
1056                event_time: zx::MonotonicInstant::from_nanos(1),
1057                trace_id: None,
1058            })
1059            .handled,
1060            Handled::No
1061        );
1062    }
1063
1064    #[fuchsia::test]
1065    fn unhandled_to_generic_conversion_preserves_fields() {
1066        const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1067        let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1068        assert_eq!(
1069            InputEvent::from(UnhandledInputEvent {
1070                device_event: InputDeviceEvent::Fake,
1071                device_descriptor: InputDeviceDescriptor::Fake,
1072                event_time: EVENT_TIME,
1073                trace_id: expected_trace_id,
1074            }),
1075            InputEvent {
1076                device_event: InputDeviceEvent::Fake,
1077                device_descriptor: InputDeviceDescriptor::Fake,
1078                event_time: EVENT_TIME,
1079                handled: Handled::No,
1080                trace_id: expected_trace_id,
1081            },
1082        );
1083    }
1084
1085    #[fuchsia::test]
1086    fn generic_to_unhandled_conversion_fails_for_handled_events() {
1087        assert_matches!(
1088            UnhandledInputEvent::try_from(InputEvent {
1089                device_event: InputDeviceEvent::Fake,
1090                device_descriptor: InputDeviceDescriptor::Fake,
1091                event_time: zx::MonotonicInstant::from_nanos(1),
1092                handled: Handled::Yes,
1093                trace_id: None,
1094            }),
1095            Err(_)
1096        )
1097    }
1098
1099    #[fuchsia::test]
1100    fn generic_to_unhandled_conversion_preserves_fields_for_unhandled_events() {
1101        const EVENT_TIME: zx::MonotonicInstant = zx::MonotonicInstant::from_nanos(42);
1102        let expected_trace_id: Option<ftrace::Id> = Some(1234.into());
1103        assert_eq!(
1104            UnhandledInputEvent::try_from(InputEvent {
1105                device_event: InputDeviceEvent::Fake,
1106                device_descriptor: InputDeviceDescriptor::Fake,
1107                event_time: EVENT_TIME,
1108                handled: Handled::No,
1109                trace_id: expected_trace_id,
1110            })
1111            .unwrap(),
1112            UnhandledInputEvent {
1113                device_event: InputDeviceEvent::Fake,
1114                device_descriptor: InputDeviceDescriptor::Fake,
1115                event_time: EVENT_TIME,
1116                trace_id: expected_trace_id,
1117            },
1118        )
1119    }
1120
1121    #[test_case(Handled::No; "initially not handled")]
1122    #[test_case(Handled::Yes; "initially handled")]
1123    fn into_handled_if_yields_handled_yes_on_true(initially_handled: Handled) {
1124        let event = InputEvent {
1125            device_event: InputDeviceEvent::Fake,
1126            device_descriptor: InputDeviceDescriptor::Fake,
1127            event_time: zx::MonotonicInstant::from_nanos(1),
1128            handled: initially_handled,
1129            trace_id: None,
1130        };
1131        pretty_assertions::assert_eq!(event.into_handled_if(true).handled, Handled::Yes);
1132    }
1133
1134    #[test_case(Handled::No; "initially not handled")]
1135    #[test_case(Handled::Yes; "initially handled")]
1136    fn into_handled_if_leaves_handled_unchanged_on_false(initially_handled: Handled) {
1137        let event = InputEvent {
1138            device_event: InputDeviceEvent::Fake,
1139            device_descriptor: InputDeviceDescriptor::Fake,
1140            event_time: zx::MonotonicInstant::from_nanos(1),
1141            handled: initially_handled.clone(),
1142            trace_id: None,
1143        };
1144        pretty_assertions::assert_eq!(event.into_handled_if(false).handled, initially_handled);
1145    }
1146
1147    #[test_case(Handled::No; "initially not handled")]
1148    #[test_case(Handled::Yes; "initially handled")]
1149    fn into_handled_yields_handled_yes(initially_handled: Handled) {
1150        let event = InputEvent {
1151            device_event: InputDeviceEvent::Fake,
1152            device_descriptor: InputDeviceDescriptor::Fake,
1153            event_time: zx::MonotonicInstant::from_nanos(1),
1154            handled: initially_handled,
1155            trace_id: None,
1156        };
1157        pretty_assertions::assert_eq!(event.into_handled().handled, Handled::Yes);
1158    }
1159}