Skip to main content

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