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