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