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