Skip to main content

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