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