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