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