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