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