Skip to main content

input_pipeline/
input_pipeline.rs

1// Copyright 2020 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::device_listener_registry::DeviceListenerRegistry;
6use crate::display_ownership::DisplayOwnership;
7use crate::focus_listener::FocusListener;
8use crate::input_device::{InputEventType, InputPipelineFeatureFlags};
9use crate::input_handler::Handler;
10use crate::{Dispatcher, Incoming, Transport, input_device, input_handler, metrics};
11use anyhow::{Context, Error, format_err};
12use fidl::endpoints::Proxy;
13use focus_chain_provider::FocusChainProviderPublisher;
14
15use fuchsia_inspect::NumericProperty;
16use fuchsia_inspect::health::Reporter;
17use fuchsia_sync::Mutex;
18use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
19use futures::future::LocalBoxFuture;
20use futures::{FutureExt, StreamExt, TryStreamExt};
21use itertools::Itertools;
22use metrics_registry::*;
23use sorted_vec_map::SortedVecMap;
24use std::rc::Rc;
25use std::sync::atomic::{AtomicU32, Ordering};
26use std::sync::{Arc, LazyLock};
27use strum::EnumCount;
28
29/// Use a self incremental u32 unique id for device_id.
30///
31/// device id start from 10 to avoid conflict with default devices in Starnix.
32/// Currently, Starnix using 0 and 1 as default devices' id. Starnix need to
33/// use default devices to deliver events from physical devices until we have
34/// API to expose device changes to UI clients.
35static NEXT_DEVICE_ID: LazyLock<AtomicU32> = LazyLock::new(|| AtomicU32::new(10));
36
37/// Each time this function is invoked, it returns the current value of its
38/// internal counter (serving as a unique id for device_id) and then increments
39/// that counter in preparation for the next call.
40fn get_next_device_id() -> u32 {
41    NEXT_DEVICE_ID.fetch_add(1, Ordering::SeqCst)
42}
43
44type BoxedInputDeviceBinding = Box<dyn input_device::InputDeviceBinding>;
45
46pub struct InputDeviceBindingAndTask {
47    pub binding: BoxedInputDeviceBinding,
48    pub task: crate::dispatcher::TaskHandle<()>,
49}
50
51/// An [`InputDeviceBindingMap`] maps an input device to one or more InputDeviceBindings and their tasks.
52/// It uses unique device id as key.
53pub type InputDeviceBindingMap = Arc<Mutex<SortedVecMap<u32, Vec<InputDeviceBindingAndTask>>>>;
54
55/// An input pipeline assembly.
56///
57/// Represents a partial stage of the input pipeline which accepts inputs through an asynchronous
58/// sender channel, and emits outputs through an asynchronous receiver channel.  Use [new] to
59/// create a new assembly.  Use [add_handler], or [add_all_handlers] to add the input pipeline
60/// handlers to use.  When done, [InputPipeline::new] can be used to make a new input pipeline.
61///
62/// # Implementation notes
63///
64/// Internally, when a new [InputPipelineAssembly] is created with multiple [InputHandler]s, the
65/// handlers are connected together using async queues.  This allows fully streamed processing of
66/// input events, and also allows some pipeline stages to generate events spontaneously, i.e.
67/// without an external stimulus.
68pub struct InputPipelineAssembly {
69    /// The top-level sender: send into this queue to inject an event into the input
70    /// pipeline.
71    sender: UnboundedSender<Vec<input_device::InputEvent>>,
72    /// The bottom-level receiver: any events that fall through the entire pipeline can
73    /// be read from this receiver.
74    receiver: UnboundedReceiver<Vec<input_device::InputEvent>>,
75
76    /// The input handlers that comprise the input pipeline.
77    handlers: Vec<Rc<dyn input_handler::BatchInputHandler>>,
78
79    /// The display ownership watcher task.
80    display_ownership_fut: Option<LocalBoxFuture<'static, ()>>,
81
82    /// The focus listener task.
83    focus_listener_fut: Option<LocalBoxFuture<'static, ()>>,
84
85    /// The metrics logger.
86    metrics_logger: metrics::MetricsLogger,
87}
88
89impl InputPipelineAssembly {
90    /// Create a new but empty [InputPipelineAssembly]. Use [add_handler] or similar
91    /// to add new handlers to it.
92    pub fn new(metrics_logger: metrics::MetricsLogger) -> Self {
93        let (sender, receiver) = mpsc::unbounded();
94        InputPipelineAssembly {
95            sender,
96            receiver,
97            handlers: vec![],
98            metrics_logger,
99            display_ownership_fut: None,
100            focus_listener_fut: None,
101        }
102    }
103
104    /// Adds another [input_handler::BatchInputHandler] into the [InputPipelineAssembly]. The handlers
105    /// are invoked in the order they are added. Returns `Self` for chaining.
106    pub fn add_handler(mut self, handler: Rc<dyn input_handler::BatchInputHandler>) -> Self {
107        self.handlers.push(handler);
108        self
109    }
110
111    /// Adds all handlers into the assembly in the order they appear in `handlers`.
112    pub fn add_all_handlers(self, handlers: Vec<Rc<dyn input_handler::BatchInputHandler>>) -> Self {
113        handlers.into_iter().fold(self, |assembly, handler| assembly.add_handler(handler))
114    }
115
116    pub fn add_display_ownership(
117        mut self,
118        display_ownership_event: zx::Event,
119        input_handlers_node: &fuchsia_inspect::Node,
120    ) -> InputPipelineAssembly {
121        let h = DisplayOwnership::new(
122            display_ownership_event,
123            input_handlers_node,
124            self.metrics_logger.clone(),
125        );
126        let metrics_logger_clone = self.metrics_logger.clone();
127        let h_clone = h.clone();
128        let sender_clone = self.sender.clone();
129        let display_ownership_fut = Box::pin(async move {
130            h_clone.clone().set_handler_healthy();
131            h_clone.clone()
132                .handle_ownership_change(sender_clone)
133                .await
134                .map_err(|e| {
135                    metrics_logger_clone.log_error(
136                        InputPipelineErrorMetricDimensionEvent::InputPipelineDisplayOwnershipIsNotSupposedToTerminate,
137                        std::format!(
138                            "display ownership is not supposed to terminate - this is likely a problem: {:?}", e));
139                        })
140                        .unwrap();
141            h_clone.set_handler_unhealthy("Receive loop terminated for handler: DisplayOwnership");
142        });
143        self.display_ownership_fut = Some(display_ownership_fut);
144        self.add_handler(h)
145    }
146
147    /// Deconstructs the assembly into constituent components, used when constructing
148    /// [InputPipeline].
149    ///
150    /// You should call [catch_unhandled] on the returned [async_channel::Receiver], and
151    /// [run] on the returned [fuchsia_async::Tasks] (or supply own equivalents).
152    fn into_components(
153        self,
154    ) -> (
155        UnboundedSender<Vec<input_device::InputEvent>>,
156        UnboundedReceiver<Vec<input_device::InputEvent>>,
157        Vec<Rc<dyn input_handler::BatchInputHandler>>,
158        metrics::MetricsLogger,
159        Option<LocalBoxFuture<'static, ()>>,
160        Option<LocalBoxFuture<'static, ()>>,
161    ) {
162        (
163            self.sender,
164            self.receiver,
165            self.handlers,
166            self.metrics_logger,
167            self.display_ownership_fut,
168            self.focus_listener_fut,
169        )
170    }
171
172    pub fn add_focus_listener(
173        mut self,
174        incoming: &Incoming,
175        focus_chain_publisher: FocusChainProviderPublisher,
176    ) -> Self {
177        let metrics_logger_clone = self.metrics_logger.clone();
178        let incoming2 = incoming.clone();
179        let focus_listener_fut = Box::pin(async move {
180            if let Ok(mut focus_listener) = FocusListener::new(
181                &incoming2,
182                focus_chain_publisher,
183                metrics_logger_clone,
184            )
185            .map_err(|e| {
186                log::warn!("could not create focus listener, focus will not be dispatched: {:?}", e)
187            }) {
188                // This will await indefinitely and process focus messages in a loop, unless there
189                // is a problem.
190                let _result = focus_listener
191                    .dispatch_focus_changes()
192                    .await
193                    .map(|_| {
194                        log::warn!("dispatch focus loop ended, focus will no longer be dispatched")
195                    })
196                    .map_err(|e| {
197                        panic!("could not dispatch focus changes, this is a fatal error: {:?}", e)
198                    });
199            }
200        });
201        self.focus_listener_fut = Some(focus_listener_fut);
202        self
203    }
204}
205
206/// An [`InputPipeline`] manages input devices and propagates input events through input handlers.
207///
208/// On creation, clients declare what types of input devices an [`InputPipeline`] manages. The
209/// [`InputPipeline`] will continuously detect new input devices of supported type(s).
210///
211/// # Example
212/// ```
213/// let ime_handler =
214///     ImeHandler::new(scene_manager.session.clone(), scene_manager.compositor_id).await?;
215/// let touch_handler = TouchHandler::new(
216///     scene_manager.session.clone(),
217///     scene_manager.compositor_id,
218///     scene_manager.display_size
219/// ).await?;
220///
221/// let assembly = InputPipelineAssembly::new()
222///     .add_handler(Box::new(ime_handler)),
223///     .add_handler(Box::new(touch_handler)),
224/// let input_pipeline = InputPipeline::new(
225///     vec![
226///         input_device::InputDeviceType::Touch,
227///         input_device::InputDeviceType::Keyboard,
228///     ],
229///     assembly,
230/// );
231/// input_pipeline.handle_input_events().await;
232/// ```
233pub struct InputPipeline {
234    /// The entry point into the input handler pipeline. Incoming input events should
235    /// be inserted into this async queue, and the input pipeline will ensure that they
236    /// are propagated through all the input handlers in the appropriate sequence.
237    pipeline_sender: UnboundedSender<Vec<input_device::InputEvent>>,
238
239    /// A clone of this sender is given to every InputDeviceBinding that this pipeline owns.
240    /// Each InputDeviceBinding will send InputEvents to the pipeline through this channel.
241    device_event_sender: UnboundedSender<Vec<input_device::InputEvent>>,
242
243    /// Receives InputEvents from all InputDeviceBindings that this pipeline owns.
244    device_event_receiver: UnboundedReceiver<Vec<input_device::InputEvent>>,
245
246    /// The types of devices this pipeline supports.
247    input_device_types: Vec<input_device::InputDeviceType>,
248
249    /// The InputDeviceBindings bound to this pipeline.
250    input_device_bindings: InputDeviceBindingMap,
251
252    /// This node is bound to the lifetime of this InputPipeline.
253    /// Inspect data will be dumped for this pipeline as long as it exists.
254    inspect_node: fuchsia_inspect::Node,
255
256    /// The metrics logger.
257    metrics_logger: metrics::MetricsLogger,
258
259    /// The feature flags for the input pipeline.
260    pub feature_flags: input_device::InputPipelineFeatureFlags,
261    /// The display ownership future, if configured.
262    display_ownership_fut: Option<LocalBoxFuture<'static, ()>>,
263
264    /// The focus listener future, if configured.
265    focus_listener_fut: Option<LocalBoxFuture<'static, ()>>,
266
267    /// The watcher future.
268    watcher_fut: Option<LocalBoxFuture<'static, ()>>,
269
270    /// The runner future.
271    runner_fut: Option<LocalBoxFuture<'static, ()>>,
272
273    /// The registry for device listeners.
274    pub device_listener_registry: DeviceListenerRegistry,
275}
276
277impl InputPipeline {
278    fn new_common(
279        input_device_types: Vec<input_device::InputDeviceType>,
280        assembly: InputPipelineAssembly,
281        inspect_node: fuchsia_inspect::Node,
282        feature_flags: input_device::InputPipelineFeatureFlags,
283    ) -> Self {
284        let (
285            pipeline_sender,
286            receiver,
287            handlers,
288            metrics_logger,
289            display_ownership_fut,
290            focus_listener_fut,
291        ) = assembly.into_components();
292
293        let mut handlers_count = handlers.len();
294        let display_ownership_fut = if let Some(fut) = display_ownership_fut {
295            handlers_count += 1;
296            Some(
297                async move {
298                    fut.await;
299                    panic!("display_ownership_fut exited unexpectedly, which compromises device state tracking. Terminating to avoid inconsistent state.");
300                }
301                .boxed_local(),
302            )
303        } else {
304            None
305        };
306
307        let focus_listener_fut = if let Some(fut) = focus_listener_fut {
308            handlers_count += 1;
309            Some(
310                async move {
311                    fut.await;
312                    panic!("focus_listener_fut exited unexpectedly, which breaks input routing. Terminating to avoid inconsistent state.");
313                }
314                .boxed_local(),
315            )
316        } else {
317            None
318        };
319
320        // Add properties to inspect node
321        inspect_node.record_string("supported_input_devices", input_device_types.iter().join(", "));
322        inspect_node.record_uint("handlers_registered", handlers_count as u64);
323        inspect_node.record_uint("handlers_healthy", handlers_count as u64);
324
325        // Initializes all handlers and starts the input pipeline loop.
326        let runner_fut = Some(InputPipeline::run(receiver, handlers, metrics_logger.clone()));
327
328        let (device_event_sender, device_event_receiver) = futures::channel::mpsc::unbounded();
329        let input_device_bindings: InputDeviceBindingMap =
330            Arc::new(Mutex::new(SortedVecMap::new()));
331        let device_listener_registry = DeviceListenerRegistry::new();
332        InputPipeline {
333            pipeline_sender,
334            device_event_sender,
335            device_event_receiver,
336            input_device_types,
337            input_device_bindings,
338            inspect_node,
339            metrics_logger,
340            feature_flags,
341            display_ownership_fut,
342            focus_listener_fut,
343            watcher_fut: None,
344            runner_fut,
345            device_listener_registry,
346        }
347    }
348
349    /// Creates a new [`InputPipeline`] for integration testing.
350    /// Unlike a production input pipeline, this pipeline will not monitor
351    /// `/svc/fuchsia.input.report.Service` for devices.
352    ///
353    /// # Parameters
354    /// - `input_device_types`: The types of devices the new [`InputPipeline`] will support.
355    /// - `assembly`: The input handlers that the [`InputPipeline`] sends InputEvents to.
356    pub fn new_for_test(
357        input_device_types: Vec<input_device::InputDeviceType>,
358        assembly: InputPipelineAssembly,
359    ) -> Self {
360        let inspector = fuchsia_inspect::Inspector::default();
361        let root = inspector.root();
362        let test_node = root.create_child("input_pipeline");
363        Self::new_common(
364            input_device_types,
365            assembly,
366            test_node,
367            input_device::InputPipelineFeatureFlags { enable_merge_touch_events: false },
368        )
369    }
370
371    /// Creates a new [`InputPipeline`] for production use.
372    ///
373    /// # Parameters
374    /// - `input_device_types`: The types of devices the new [`InputPipeline`] will support.
375    /// - `assembly`: The input handlers that the [`InputPipeline`] sends InputEvents to.
376    /// - `inspect_node`: The root node for InputPipeline's Inspect tree
377    pub fn new(
378        incoming: &Incoming,
379        input_device_types: Vec<input_device::InputDeviceType>,
380        assembly: InputPipelineAssembly,
381        inspect_node: fuchsia_inspect::Node,
382        feature_flags: input_device::InputPipelineFeatureFlags,
383        metrics_logger: metrics::MetricsLogger,
384    ) -> Result<Self, Error> {
385        let mut input_pipeline =
386            Self::new_common(input_device_types, assembly, inspect_node, feature_flags);
387        let input_device_types = input_pipeline.input_device_types.clone();
388        let input_event_sender = input_pipeline.device_event_sender.clone();
389        let input_device_bindings = input_pipeline.input_device_bindings.clone();
390        let device_listener_registry = input_pipeline.device_listener_registry.clone();
391        let devices_node = input_pipeline.inspect_node.create_child("input_devices");
392        let devices_node_weak = devices_node.clone_weak();
393        input_pipeline.inspect_node.record(devices_node);
394        let feature_flags = input_pipeline.feature_flags.clone();
395        let incoming = incoming.clone();
396        let watcher_fut = async move {
397            let devices_discovered = devices_node_weak.create_uint("devices_discovered", 0);
398            let devices_connected = devices_node_weak.create_uint("devices_connected", 0);
399            // Watches the input device service for new input devices. Creates new InputDeviceBindings
400            // that send InputEvents to `input_event_receiver`.
401            match async {
402                let service = incoming
403                    .open_service(fidl_fuchsia_input_report::ServiceMarker)
404                    .context("failed to open service")?;
405                Self::watch_for_devices(
406                    service,
407                    input_device_types,
408                    input_event_sender,
409                    input_device_bindings,
410                    &devices_node_weak,
411                    &devices_discovered,
412                    &devices_connected,
413                    false, /* break_on_idle */
414                    feature_flags,
415                    metrics_logger.clone(),
416                    device_listener_registry,
417                )
418                .await
419                .context("failed to watch for devices")
420            }
421            .await
422            {
423                Ok(()) => {}
424                Err(err) => {
425                    // This error is usually benign in tests: it means that the setup does not
426                    // support dynamic device discovery. Almost no tests support dynamic
427                    // device discovery, and they also do not need those.
428                    metrics_logger.log_warn(
429                        InputPipelineErrorMetricDimensionEvent::InputPipelineUnableToWatchForNewInputDevices,
430                        std::format!(
431                            "Input pipeline is unable to watch for new input devices: {:?}",
432                            err
433                        ));
434                }
435            }
436            devices_node_weak.record(devices_discovered);
437            devices_node_weak.record(devices_connected);
438        }.boxed_local();
439
440        input_pipeline.watcher_fut = Some(watcher_fut);
441
442        Ok(input_pipeline)
443    }
444
445    /// Gets the input device bindings.
446    pub fn input_device_bindings(&self) -> &InputDeviceBindingMap {
447        &self.input_device_bindings
448    }
449
450    /// Gets the input device sender: this is the channel that should be cloned
451    /// and used for injecting events from the drivers into the input pipeline.
452    pub fn input_event_sender(&self) -> &UnboundedSender<Vec<input_device::InputEvent>> {
453        &self.device_event_sender
454    }
455
456    /// Gets a list of input device types supported by this input pipeline.
457    pub fn input_device_types(&self) -> &[input_device::InputDeviceType] {
458        &self.input_device_types
459    }
460
461    /// Gets the device listener registry.
462    pub fn device_listener_registry(&self) -> &DeviceListenerRegistry {
463        &self.device_listener_registry
464    }
465
466    /// Forwards all input events into the input pipeline.
467    pub async fn handle_input_events(self) {
468        let metrics_logger_clone = self.metrics_logger.clone();
469        let mut device_event_receiver = self.device_event_receiver;
470        let pipeline_sender = self.pipeline_sender.clone();
471
472        let forwarder = async move {
473            while let Some(input_event) = device_event_receiver.next().await {
474                if let Err(e) = pipeline_sender.unbounded_send(input_event) {
475                    metrics_logger_clone.log_error(
476                        InputPipelineErrorMetricDimensionEvent::InputPipelineCouldNotForwardEventFromDriver,
477                        std::format!("could not forward event from driver: {:?}", e));
478                }
479            }
480
481            metrics_logger_clone.log_error(
482                InputPipelineErrorMetricDimensionEvent::InputPipelineStopHandlingEvents,
483                "Input pipeline stopped handling input events.".to_string(),
484            );
485        }.boxed_local();
486
487        let tasks = crate::task::InputPipelineTasks {
488            watcher: self.watcher_fut.unwrap_or_else(|| Box::pin(futures::future::pending())),
489            runner: self.runner_fut.unwrap_or_else(|| Box::pin(futures::future::pending())),
490            display_ownership: self.display_ownership_fut,
491            focus_listener: self.focus_listener_fut,
492            forwarder,
493        };
494
495        tasks.run().await;
496    }
497
498    async fn connect_and_bind_device(
499        instance: &fidl_fuchsia_input_report::ServiceProxy,
500        device_types: &[input_device::InputDeviceType],
501        input_event_sender: &UnboundedSender<Vec<input_device::InputEvent>>,
502        bindings: &InputDeviceBindingMap,
503        input_devices_node: &fuchsia_inspect::Node,
504        devices_discovered: &fuchsia_inspect::UintProperty,
505        devices_connected: &fuchsia_inspect::UintProperty,
506        feature_flags: input_device::InputPipelineFeatureFlags,
507        metrics_logger: metrics::MetricsLogger,
508        device_listener_registry: DeviceListenerRegistry,
509    ) {
510        let filename = instance.instance_name().to_string();
511        log::info!("found input device {}", filename);
512        devices_discovered.add(1);
513
514        let res = async {
515            let device_proxy =
516                instance.connect_to_input_device().context("connect to input device")?;
517            let channel = device_proxy
518                .into_client_end()
519                .map_err(|_| format_err!("failed to get client end"))?
520                .into_channel();
521            let device_client = fidl_next::ClientEnd::<
522                fidl_next_fuchsia_input_report::InputDevice,
523                zx::Channel,
524            >::from_untyped(channel);
525            let device_client = Dispatcher::client_from_zx_channel(device_client);
526            let (device_client, join_handle) = device_client.spawn_full();
527            let device_id = get_next_device_id();
528            if let Some(descriptor) = add_device_bindings(
529                device_types,
530                &filename,
531                device_client,
532                input_event_sender,
533                bindings,
534                device_id,
535                input_devices_node,
536                Some(devices_connected),
537                feature_flags,
538                metrics_logger,
539                false,
540            )
541            .await
542            {
543                device_listener_registry.notify_device_changed(
544                    fidl_next_fuchsia_ui_input::Action::Added,
545                    device_id,
546                    descriptor.clone(),
547                );
548                let device_listener_registry_clone = device_listener_registry.clone();
549                let bindings_clone = bindings.clone();
550                let watch_task = Dispatcher::spawn_local(async move {
551                    let _ = join_handle.await;
552                    log::info!("Device {} disconnected", device_id);
553                    device_listener_registry_clone.notify_device_changed(
554                        fidl_next_fuchsia_ui_input::Action::Removed,
555                        device_id,
556                        descriptor,
557                    );
558                    bindings_clone.lock().remove(&device_id);
559                });
560                watch_task.detach();
561            }
562            Ok::<(), Error>(())
563        }
564        .await;
565
566        if let Err(e) = res {
567            log::error!("Failed to connect and bind input device {}: {:?}", filename, e);
568        }
569    }
570
571    /// Watches the input report service for new input devices. Creates InputDeviceBindings
572    /// if new devices match a type in `device_types`.
573    ///
574    /// # Parameters
575    /// - `service`: The service to watch for new devices.
576    /// - `device_types`: The types of devices to watch for.
577    /// - `input_event_sender`: The channel new InputDeviceBindings will send InputEvents to.
578    /// - `bindings`: Holds all the InputDeviceBindings
579    /// - `input_devices_node`: The parent node for all device bindings' inspect nodes.
580    /// - `devices_discovered`: Inspect property to track discovered devices.
581    /// - `devices_connected`: Inspect property to track connected devices.
582    /// - `break_on_idle`: If true, stops watching for devices once all existing devices are handled.
583    /// - `feature_flags`: The feature flags.
584    /// - `metrics_logger`: The metrics logger.
585    ///
586    /// # Errors
587    /// If the input report service or a connection within it fails.
588    async fn watch_for_devices(
589        service: fuchsia_component::client::Service<fidl_fuchsia_input_report::ServiceMarker>,
590        device_types: Vec<input_device::InputDeviceType>,
591        input_event_sender: UnboundedSender<Vec<input_device::InputEvent>>,
592        bindings: InputDeviceBindingMap,
593        input_devices_node: &fuchsia_inspect::Node,
594        devices_discovered: &fuchsia_inspect::UintProperty,
595        devices_connected: &fuchsia_inspect::UintProperty,
596        break_on_idle: bool,
597        feature_flags: input_device::InputPipelineFeatureFlags,
598        metrics_logger: metrics::MetricsLogger,
599        device_listener_registry: DeviceListenerRegistry,
600    ) -> Result<(), Error> {
601        if break_on_idle {
602            let instances = service.enumerate().await?;
603            for instance in instances {
604                Self::connect_and_bind_device(
605                    &instance,
606                    &device_types,
607                    &input_event_sender,
608                    &bindings,
609                    input_devices_node,
610                    devices_discovered,
611                    devices_connected,
612                    feature_flags.clone(),
613                    metrics_logger.clone(),
614                    device_listener_registry.clone(),
615                )
616                .await;
617            }
618            Ok(())
619        } else {
620            let mut instances = service
621                .watch()
622                .await
623                .context("failed to watch input-report service")?
624                .err_into::<Error>()
625                .boxed();
626
627            while let Some(instance) = instances.try_next().await? {
628                Self::connect_and_bind_device(
629                    &instance,
630                    &device_types,
631                    &input_event_sender,
632                    &bindings,
633                    input_devices_node,
634                    devices_discovered,
635                    devices_connected,
636                    feature_flags.clone(),
637                    metrics_logger.clone(),
638                    device_listener_registry.clone(),
639                )
640                .await;
641            }
642
643            Err(format_err!("Input pipeline stopped watching for new input devices."))
644        }
645    }
646
647    /// Handles the incoming InputDeviceRegistryRequestStream.
648    ///
649    /// This method will end when the request stream is closed. If the stream closes with an
650    /// error the error will be returned in the Result.
651    ///
652    /// **NOTE**: Only one stream is handled at a time. https://fxbug.dev/42061078
653    ///
654    /// # Parameters
655    /// - `stream`: The stream of InputDeviceRegistryRequests.
656    /// - `device_types`: The types of devices to watch for.
657    /// - `input_event_sender`: The channel new InputDeviceBindings will send InputEvents to.
658    /// - `bindings`: Holds all the InputDeviceBindings associated with the InputPipeline.
659    /// - `input_devices_node`: The parent node for all injected devices' inspect nodes.
660    /// - `metrics_logger`: The metrics logger.
661    pub async fn handle_input_device_registry_request_stream(
662        mut stream: fidl_fuchsia_input_injection::InputDeviceRegistryRequestStream,
663        device_types: &[input_device::InputDeviceType],
664        input_event_sender: &UnboundedSender<Vec<input_device::InputEvent>>,
665        bindings: &InputDeviceBindingMap,
666        input_devices_node: &fuchsia_inspect::Node,
667        feature_flags: input_device::InputPipelineFeatureFlags,
668        metrics_logger: metrics::MetricsLogger,
669        device_listener_registry: DeviceListenerRegistry,
670        task_sender: UnboundedSender<crate::dispatcher::TaskHandle<()>>,
671    ) -> Result<(), Error> {
672        while let Some(request) = stream
673            .try_next()
674            .await
675            .context("Error handling input device registry request stream")?
676        {
677            match request {
678                fidl_fuchsia_input_injection::InputDeviceRegistryRequest::Register {
679                    device,
680                    ..
681                } => {
682                    // Add a binding if the device is a type being tracked
683                    let device = fidl_next::ClientEnd::<
684                        fidl_next_fuchsia_input_report::InputDevice,
685                        zx::Channel,
686                    >::from_untyped(device.into_channel());
687                    let device = Dispatcher::client_from_zx_channel(device);
688                    let (device, join_handle) = device.spawn_full();
689                    let device_id = get_next_device_id();
690
691                    if let Some(descriptor) = add_device_bindings(
692                        device_types,
693                        &format!("input-device-registry-{}", device_id),
694                        device,
695                        input_event_sender,
696                        bindings,
697                        device_id,
698                        input_devices_node,
699                        None,
700                        feature_flags.clone(),
701                        metrics_logger.clone(),
702                        true,
703                    )
704                    .await
705                    {
706                        device_listener_registry.notify_device_changed(
707                            fidl_next_fuchsia_ui_input::Action::Added,
708                            device_id,
709                            descriptor.clone(),
710                        );
711
712                        let device_listener_registry_clone = device_listener_registry.clone();
713                        let bindings_clone = bindings.clone();
714                        let watch_task = Dispatcher::spawn_local(async move {
715                            let _ = join_handle.await;
716                            log::info!("Injected device {} disconnected", device_id);
717                            device_listener_registry_clone.notify_device_changed(
718                                fidl_next_fuchsia_ui_input::Action::Removed,
719                                device_id,
720                                descriptor,
721                            );
722                            bindings_clone.lock().remove(&device_id);
723                        });
724                        let _ = task_sender.unbounded_send(watch_task);
725                    }
726                }
727                fidl_fuchsia_input_injection::InputDeviceRegistryRequest::RegisterAndGetDeviceInfo {
728                    device,
729                    responder,
730                    .. } => {
731                    // Add a binding if the device is a type being tracked
732                    let device = fidl_next::ClientEnd::<
733                        fidl_next_fuchsia_input_report::InputDevice,
734                        zx::Channel,
735                    >::from_untyped(device.into_channel());
736                    let device = Dispatcher::client_from_zx_channel(device);
737                    let (device, join_handle) = device.spawn_full();
738                    let device_id = get_next_device_id();
739
740                    let descriptor_opt = add_device_bindings(
741                        device_types,
742                        &format!("input-device-registry-{}", device_id),
743                        device,
744                        input_event_sender,
745                        bindings,
746                        device_id,
747                        input_devices_node,
748                        None,
749                        feature_flags.clone(),
750                        metrics_logger.clone(),
751                        true,
752                    )
753                    .await;
754
755                    if let Some(descriptor) = descriptor_opt {
756                        device_listener_registry.notify_device_changed(
757                            fidl_next_fuchsia_ui_input::Action::Added,
758                            device_id,
759                            descriptor.clone(),
760                        );
761
762                        let device_listener_registry_clone = device_listener_registry.clone();
763                        let bindings_clone = bindings.clone();
764                        let watch_task = Dispatcher::spawn_local(async move {
765                            let _ = join_handle.await;
766                            log::info!("Injected device {} disconnected", device_id);
767                            device_listener_registry_clone.notify_device_changed(
768                                fidl_next_fuchsia_ui_input::Action::Removed,
769                                device_id,
770                                descriptor.clone(),
771                            );
772                            bindings_clone.lock().remove(&device_id);
773                        });
774                        let _ = task_sender.unbounded_send(watch_task);
775                    }
776
777                    responder.send(fidl_fuchsia_input_injection::InputDeviceRegistryRegisterAndGetDeviceInfoResponse{
778                        device_id: Some(device_id),
779                        ..Default::default()
780                    }).expect("Failed to respond to RegisterAndGetDeviceInfo request");
781                }
782            }
783        }
784
785        Ok(())
786    }
787
788    /// Initializes all handlers and starts the input pipeline loop in an asynchronous executor.
789    fn run(
790        mut receiver: UnboundedReceiver<Vec<input_device::InputEvent>>,
791        handlers: Vec<Rc<dyn input_handler::BatchInputHandler>>,
792        metrics_logger: metrics::MetricsLogger,
793    ) -> LocalBoxFuture<'static, ()> {
794        async move {
795            for handler in &handlers {
796                handler.clone().set_handler_healthy();
797            }
798
799            let mut handlers_by_type: [Vec<Rc<dyn input_handler::BatchInputHandler>>;
800                InputEventType::COUNT] = Default::default();
801
802            // TODO: b/478262850 - We can use supported_input_devices to populate this list.
803            let event_types = vec![
804                InputEventType::Keyboard,
805                InputEventType::LightSensor,
806                InputEventType::ConsumerControls,
807                InputEventType::Mouse,
808                InputEventType::TouchScreen,
809                InputEventType::Touchpad,
810                #[cfg(test)]
811                InputEventType::Fake,
812            ];
813
814            for event_type in event_types {
815                let handlers_for_type: Vec<Rc<dyn input_handler::BatchInputHandler>> = handlers
816                    .iter()
817                    .filter(|h| h.interest().contains(&event_type))
818                    .cloned()
819                    .collect();
820                handlers_by_type[event_type as usize] = handlers_for_type;
821            }
822
823            while let Some(events) = receiver.next().await {
824                if events.is_empty() {
825                    continue;
826                }
827
828                let mut groups_seen = 0;
829                let events = events.into_iter().chunk_by(|e| InputEventType::from(&e.device_event));
830                let events = events.into_iter().map(|(k, v)| (k, v.collect::<Vec<_>>()));
831                for (event_type, event_group) in events {
832                    groups_seen += 1;
833                    if groups_seen == 2 {
834                        metrics_logger.log_error(
835                                InputPipelineErrorMetricDimensionEvent::InputFrameContainsMultipleTypesOfEvents,
836                                "it is not recommended to contain multiple types of events in 1 send".to_string(),
837                            );
838                    }
839                    let mut events_in_group = event_group;
840
841                    // Get pre-computed handlers for this event type.
842                    let handlers = &handlers_by_type[event_type as usize];
843
844                    for handler in handlers {
845                        events_in_group =
846                            handler.clone().handle_input_events(events_in_group).await;
847                    }
848
849                    for event in events_in_group {
850                        if event.handled == input_device::Handled::No {
851                            log::warn!("unhandled input event: {:?}", event);
852                        }
853                        if let Some(trace_id) = event.trace_id {
854                            fuchsia_trace::flow_end!(
855                                "input",
856                                "event_in_input_pipeline",
857                                trace_id.into()
858                            );
859                        }
860                    }
861                }
862            }
863            for handler in &handlers {
864                handler.clone().set_handler_unhealthy("Pipeline loop terminated");
865            }
866            panic!("Runner task is not supposed to terminate.")
867        }
868        .boxed_local()
869    }
870}
871
872/// Adds `InputDeviceBinding`s to `bindings` for all `device_types` exposed by `device_proxy`.
873///
874/// # Parameters
875/// - `device_types`: The types of devices to watch for.
876/// - `device_proxy`: A proxy to the input device.
877/// - `input_event_sender`: The channel new InputDeviceBindings will send InputEvents to.
878/// - `bindings`: Holds all the InputDeviceBindings associated with the InputPipeline.
879/// - `device_id`: The device id of the associated bindings.
880/// - `input_devices_node`: The parent node for all device bindings' inspect nodes.
881///
882/// # Note
883/// This will create multiple bindings, in the case where
884/// * `device_proxy().get_descriptor()` returns a `fidl_fuchsia_input_report::DeviceDescriptor`
885///   with multiple table fields populated, and
886/// * multiple populated table fields correspond to device types present in `device_types`
887///
888/// This is used, for example, to support the Atlas touchpad. In that case, a single
889/// instance of `fuchsia.input.report.Service` provides both a `fuchsia.input.report.MouseDescriptor` and
890/// a `fuchsia.input.report.TouchDescriptor`.
891async fn add_device_bindings(
892    device_types: &[input_device::InputDeviceType],
893    instance_name: &str,
894    device_proxy: fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, Transport>,
895    input_event_sender: &UnboundedSender<Vec<input_device::InputEvent>>,
896    bindings: &InputDeviceBindingMap,
897    device_id: u32,
898    input_devices_node: &fuchsia_inspect::Node,
899    devices_connected: Option<&fuchsia_inspect::UintProperty>,
900    feature_flags: InputPipelineFeatureFlags,
901    metrics_logger: metrics::MetricsLogger,
902    is_injected: bool,
903) -> Option<fidl_next_fuchsia_input_report::DeviceDescriptor> {
904    let mut matched_device_types = vec![];
905    let descriptor = match device_proxy.get_descriptor().await {
906        Ok(res) => {
907            for device_type in device_types {
908                if input_device::is_device_type(&res.descriptor, *device_type).await {
909                    matched_device_types.push(device_type);
910                    match devices_connected {
911                        Some(dev_connected) => {
912                            let _ = dev_connected.add(1);
913                        }
914                        None => (),
915                    };
916                }
917            }
918            if matched_device_types.is_empty() {
919                log::info!(
920                    "device {} did not match any supported device types: {:?}",
921                    instance_name,
922                    device_types
923                );
924                let device_node =
925                    input_devices_node.create_child(format!("{}_Unsupported", instance_name));
926                let mut health = fuchsia_inspect::health::Node::new(&device_node);
927                health.set_unhealthy("Unsupported device type.");
928                device_node.record(health);
929                input_devices_node.record(device_node);
930                return None;
931            }
932            res.descriptor
933        }
934        Err(_) => {
935            metrics_logger.clone().log_error(
936                InputPipelineErrorMetricDimensionEvent::InputPipelineNoDeviceDescriptor,
937                std::format!("cannot bind device {} without a device descriptor", instance_name),
938            );
939            return None;
940        }
941    };
942
943    log::info!(
944        "binding {} to device types: {}",
945        instance_name,
946        matched_device_types
947            .iter()
948            .fold(String::new(), |device_types_string, device_type| device_types_string
949                + &format!("{:?}, ", device_type))
950    );
951
952    let mut new_bindings: Vec<InputDeviceBindingAndTask> = vec![];
953    for device_type in matched_device_types {
954        // Clone `device_proxy`, so that multiple bindings (e.g. a `MouseBinding` and a
955        // `TouchBinding`) can read data from the same `fuchsia.input.report.Service` instance.
956        //
957        // There's no conflict in having multiple bindings read from the same node,
958        // since:
959        // * each binding will create its own `fuchsia.input.report.InputReportsReader`, and
960        // * the device driver will copy each incoming report to each connected reader.
961        //
962        // This does mean that reports from the Atlas touchpad device get read twice
963        // (by a `MouseBinding` and a `TouchBinding`), regardless of whether the device
964        // is operating in mouse mode or touchpad mode.
965        //
966        // This hasn't been an issue because:
967        // * Semantically: things are fine, because each binding discards irrelevant reports.
968        //   (E.g. `MouseBinding` discards anything that isn't a `MouseInputReport`), and
969        // * Performance wise: things are fine, because the data rate of the touchpad is low
970        //   (125 HZ).
971        //
972        // If we add additional cases where bindings share an underlying service instance,
973        // we might consider adding a multiplexing binding, to avoid reading duplicate reports.
974        let proxy = device_proxy.clone();
975        let device_node =
976            input_devices_node.create_child(format!("{}_{}", instance_name, device_type));
977        match input_device::get_device_binding(
978            *device_type,
979            proxy,
980            device_id,
981            input_event_sender.clone(),
982            device_node,
983            feature_flags.clone(),
984            metrics_logger.clone(),
985            is_injected,
986        )
987        .await
988        {
989            Ok((binding, task)) => new_bindings.push(InputDeviceBindingAndTask { binding, task }),
990            Err(e) => {
991                metrics_logger.log_error(
992                    InputPipelineErrorMetricDimensionEvent::InputPipelineFailedToBind,
993                    std::format!("failed to bind {} as {:?}: {}", instance_name, device_type, e),
994                );
995            }
996        }
997    }
998
999    if !new_bindings.is_empty() {
1000        let mut bindings = bindings.lock();
1001        if let Some(v) = bindings.get_mut(&device_id) {
1002            v.extend(new_bindings);
1003        } else {
1004            bindings.insert(device_id, new_bindings);
1005        }
1006        Some(descriptor)
1007    } else {
1008        None
1009    }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014    use super::*;
1015    use crate::dispatcher::Transport;
1016    use fidl_next::Request;
1017
1018    struct MockDeviceListener {
1019        event_sender:
1020            futures::channel::mpsc::UnboundedSender<fidl_next_fuchsia_ui_input::DeviceEvent>,
1021    }
1022
1023    impl fidl_next_fuchsia_ui_input::DeviceListenerServerHandler<Transport> for MockDeviceListener {
1024        async fn on_device_changed(
1025            &mut self,
1026            request: Request<
1027                fidl_next_fuchsia_ui_input::device_listener::OnDeviceChanged,
1028                Transport,
1029            >,
1030        ) {
1031            let event = request.payload().event.clone();
1032            let _ = self.event_sender.unbounded_send(event);
1033        }
1034    }
1035    use crate::input_device::{InputDeviceBinding, InputEventType};
1036    use crate::utils::Position;
1037    use crate::{fake_input_device_binding, mouse_binding, observe_fake_events_input_handler};
1038    use async_trait::async_trait;
1039    use diagnostics_assertions::AnyProperty;
1040    use fidl::endpoints::{create_proxy_and_stream, create_request_stream};
1041    use fidl_fuchsia_io as fio;
1042    use fuchsia_async as fasync;
1043    use futures::{FutureExt, StreamExt};
1044    use pretty_assertions::assert_eq;
1045    use rand::RngExt as _;
1046    use sorted_vec_map::SortedVecSet;
1047    use vfs::{pseudo_directory, service as pseudo_fs_service};
1048
1049    /// Returns the InputEvent sent over `sender`.
1050    ///
1051    /// # Parameters
1052    /// - `sender`: The channel to send the InputEvent over.
1053    fn send_input_event(
1054        sender: UnboundedSender<Vec<input_device::InputEvent>>,
1055    ) -> Vec<input_device::InputEvent> {
1056        let mut rng = rand::rng();
1057        let offset =
1058            Position { x: rng.random_range(0..10) as f32, y: rng.random_range(0..10) as f32 };
1059        let input_event = input_device::InputEvent {
1060            device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent::new(
1061                mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1062                    counts: Position { x: offset.x, y: offset.y },
1063                }),
1064                None, /* wheel_delta_v */
1065                None, /* wheel_delta_h */
1066                mouse_binding::MousePhase::Move,
1067                SortedVecSet::new(),
1068                SortedVecSet::new(),
1069                None, /* is_precision_scroll */
1070                None, /* wake_lease */
1071            )),
1072            device_descriptor: input_device::InputDeviceDescriptor::Mouse(
1073                mouse_binding::MouseDeviceDescriptor {
1074                    device_id: 1,
1075                    absolute_x_range: None,
1076                    absolute_y_range: None,
1077                    wheel_v_range: None,
1078                    wheel_h_range: None,
1079                    buttons: None,
1080                },
1081            ),
1082            event_time: zx::MonotonicInstant::get(),
1083            handled: input_device::Handled::No,
1084            trace_id: None,
1085        };
1086        match sender.unbounded_send(vec![input_event.clone()]) {
1087            Err(_) => assert!(false),
1088            _ => {}
1089        }
1090
1091        vec![input_event]
1092    }
1093
1094    /// Returns a MouseDescriptor on an InputDeviceRequest.
1095    ///
1096    /// # Parameters
1097    /// - `input_device_request`: The request to handle.
1098    fn handle_input_device_request(
1099        input_device_request: fidl_fuchsia_input_report::InputDeviceRequest,
1100    ) {
1101        match input_device_request {
1102            fidl_fuchsia_input_report::InputDeviceRequest::GetDescriptor { responder } => {
1103                let _ = responder.send(&fidl_fuchsia_input_report::DeviceDescriptor {
1104                    device_information: None,
1105                    mouse: Some(fidl_fuchsia_input_report::MouseDescriptor {
1106                        input: Some(fidl_fuchsia_input_report::MouseInputDescriptor {
1107                            movement_x: None,
1108                            movement_y: None,
1109                            scroll_v: None,
1110                            scroll_h: None,
1111                            buttons: Some(vec![0]),
1112                            position_x: None,
1113                            position_y: None,
1114                            ..Default::default()
1115                        }),
1116                        ..Default::default()
1117                    }),
1118                    sensor: None,
1119                    touch: None,
1120                    keyboard: None,
1121                    consumer_control: None,
1122                    ..Default::default()
1123                });
1124            }
1125            _ => {}
1126        }
1127    }
1128
1129    /// Tests that an input pipeline handles events from multiple devices.
1130    #[fuchsia::test]
1131    async fn multiple_devices_single_handler() {
1132        // Create two fake device bindings.
1133        let (device_event_sender, device_event_receiver) = futures::channel::mpsc::unbounded();
1134        let first_device_binding =
1135            fake_input_device_binding::FakeInputDeviceBinding::new(device_event_sender.clone());
1136        let second_device_binding =
1137            fake_input_device_binding::FakeInputDeviceBinding::new(device_event_sender.clone());
1138
1139        // Create a fake input handler.
1140        let (handler_event_sender, mut handler_event_receiver) =
1141            futures::channel::mpsc::channel(100);
1142        let input_handler = observe_fake_events_input_handler::ObserveFakeEventsInputHandler::new(
1143            handler_event_sender,
1144        );
1145
1146        // Build the input pipeline.
1147        let (sender, receiver, handlers, _, _, _) =
1148            InputPipelineAssembly::new(metrics::MetricsLogger::default())
1149                .add_handler(input_handler)
1150                .into_components();
1151        let inspector = fuchsia_inspect::Inspector::default();
1152        let test_node = inspector.root().create_child("input_pipeline");
1153        let runner_fut =
1154            Some(InputPipeline::run(receiver, handlers, metrics::MetricsLogger::default()));
1155        let input_pipeline = InputPipeline {
1156            pipeline_sender: sender,
1157            device_event_sender,
1158            device_event_receiver,
1159            input_device_types: vec![],
1160            input_device_bindings: Arc::new(Mutex::new(SortedVecMap::new())),
1161            inspect_node: test_node,
1162            metrics_logger: metrics::MetricsLogger::default(),
1163            feature_flags: input_device::InputPipelineFeatureFlags::default(),
1164            display_ownership_fut: None,
1165            focus_listener_fut: None,
1166            watcher_fut: None,
1167            runner_fut,
1168            device_listener_registry: DeviceListenerRegistry::new(),
1169        };
1170
1171        // Send an input event from each device.
1172        let first_device_events = send_input_event(first_device_binding.input_event_sender());
1173        let second_device_events = send_input_event(second_device_binding.input_event_sender());
1174
1175        // Run the pipeline.
1176        let _pipeline_task = fasync::Task::local(input_pipeline.handle_input_events());
1177
1178        // Assert the handler receives the events.
1179        let first_handled_event = handler_event_receiver.next().await;
1180        assert_eq!(first_handled_event, first_device_events.into_iter().next());
1181
1182        let second_handled_event = handler_event_receiver.next().await;
1183        assert_eq!(second_handled_event, second_device_events.into_iter().next());
1184    }
1185
1186    /// Tests that an input pipeline handles events through multiple input handlers.
1187    #[fuchsia::test]
1188    async fn single_device_multiple_handlers() {
1189        // Create two fake device bindings.
1190        let (device_event_sender, device_event_receiver) = futures::channel::mpsc::unbounded();
1191        let input_device_binding =
1192            fake_input_device_binding::FakeInputDeviceBinding::new(device_event_sender.clone());
1193
1194        // Create two fake input handlers.
1195        let (first_handler_event_sender, mut first_handler_event_receiver) =
1196            futures::channel::mpsc::channel(100);
1197        let first_input_handler =
1198            observe_fake_events_input_handler::ObserveFakeEventsInputHandler::new(
1199                first_handler_event_sender,
1200            );
1201        let (second_handler_event_sender, mut second_handler_event_receiver) =
1202            futures::channel::mpsc::channel(100);
1203        let second_input_handler =
1204            observe_fake_events_input_handler::ObserveFakeEventsInputHandler::new(
1205                second_handler_event_sender,
1206            );
1207
1208        // Build the input pipeline.
1209        let (sender, receiver, handlers, _, _, _) =
1210            InputPipelineAssembly::new(metrics::MetricsLogger::default())
1211                .add_handler(first_input_handler)
1212                .add_handler(second_input_handler)
1213                .into_components();
1214        let inspector = fuchsia_inspect::Inspector::default();
1215        let test_node = inspector.root().create_child("input_pipeline");
1216        let runner_fut =
1217            Some(InputPipeline::run(receiver, handlers, metrics::MetricsLogger::default()));
1218        let input_pipeline = InputPipeline {
1219            pipeline_sender: sender,
1220            device_event_sender,
1221            device_event_receiver,
1222            input_device_types: vec![],
1223            input_device_bindings: Arc::new(Mutex::new(SortedVecMap::new())),
1224            inspect_node: test_node,
1225            metrics_logger: metrics::MetricsLogger::default(),
1226            feature_flags: input_device::InputPipelineFeatureFlags::default(),
1227            display_ownership_fut: None,
1228            focus_listener_fut: None,
1229            watcher_fut: None,
1230            runner_fut,
1231            device_listener_registry: DeviceListenerRegistry::new(),
1232        };
1233
1234        // Send an input event.
1235        let input_events = send_input_event(input_device_binding.input_event_sender());
1236
1237        // Run the pipeline.
1238        let _pipeline_task = fasync::Task::local(input_pipeline.handle_input_events());
1239
1240        // Assert both handlers receive the event.
1241        let expected_event = input_events.into_iter().next();
1242        let first_handler_event = first_handler_event_receiver.next().await;
1243        assert_eq!(first_handler_event, expected_event);
1244        let second_handler_event = second_handler_event_receiver.next().await;
1245        assert_eq!(second_handler_event, expected_event);
1246    }
1247
1248    /// Tests that a single mouse device binding is created for the one input device in the
1249    /// input report service directory.
1250    #[fuchsia::test]
1251    async fn watch_devices_one_match_exists() {
1252        let dir = pseudo_directory! {
1253            "fuchsia.input.report.Service" => pseudo_directory! {
1254                "instance_0" => pseudo_directory! {
1255                    "input_device" => pseudo_fs_service::host(
1256                        move |mut request_stream: fidl_fuchsia_input_report::InputDeviceRequestStream| {
1257                            async move {
1258                                while let Some(input_device_request) =
1259                                    request_stream.try_next().await.unwrap()
1260                                {
1261                                    handle_input_device_request(input_device_request);
1262                                }
1263
1264                            }.boxed()
1265                        },
1266                    )
1267                }
1268            }
1269        };
1270
1271        // Get a proxy to the pseudo directory for the input pipeline. The input pipeline uses this
1272        // proxy to get connections to input devices.
1273        let svc_proxy =
1274            vfs::directory::serve_read_only(dir, vfs::execution_scope::ExecutionScope::new());
1275
1276        let (input_event_sender, _input_event_receiver) = futures::channel::mpsc::unbounded();
1277        let bindings: InputDeviceBindingMap = Arc::new(Mutex::new(SortedVecMap::new()));
1278        let supported_device_types = vec![input_device::InputDeviceType::Mouse];
1279
1280        let inspector = fuchsia_inspect::Inspector::default();
1281        let test_node = inspector.root().create_child("input_pipeline");
1282        test_node.record_string(
1283            "supported_input_devices",
1284            supported_device_types.clone().iter().join(", "),
1285        );
1286        let input_devices = test_node.create_child("input_devices");
1287        // Assert that inspect tree is initialized with no devices.
1288        diagnostics_assertions::assert_data_tree!(inspector, root: {
1289            input_pipeline: {
1290                supported_input_devices: "Mouse",
1291                input_devices: {}
1292            }
1293        });
1294
1295        let devices_discovered = input_devices.create_uint("devices_discovered", 0);
1296        let devices_connected = input_devices.create_uint("devices_connected", 0);
1297
1298        let dir = fuchsia_fs::directory::open_directory_async(
1299            &svc_proxy,
1300            "fuchsia.input.report.Service",
1301            fio::PERM_READABLE,
1302        )
1303        .expect("open service directory");
1304        let service = fuchsia_component::client::Service::from_service_dir_proxy(
1305            dir,
1306            fidl_fuchsia_input_report::ServiceMarker,
1307        );
1308
1309        let _ = InputPipeline::watch_for_devices(
1310            service,
1311            supported_device_types,
1312            input_event_sender,
1313            bindings.clone(),
1314            &input_devices,
1315            &devices_discovered,
1316            &devices_connected,
1317            true, /* break_on_idle */
1318            InputPipelineFeatureFlags { enable_merge_touch_events: false },
1319            metrics::MetricsLogger::default(),
1320            DeviceListenerRegistry::new(),
1321        )
1322        .await;
1323
1324        // Assert that one mouse device with accurate device id was found.
1325        {
1326            let bindings_map = bindings.lock();
1327            assert_eq!(bindings_map.len(), 1);
1328            let bindings_vector = bindings_map.get(&10);
1329            assert!(bindings_vector.is_some());
1330            assert_eq!(bindings_vector.unwrap().len(), 1);
1331            let boxed_mouse_binding = bindings_vector.unwrap().get(0);
1332            assert!(boxed_mouse_binding.is_some());
1333            assert_eq!(
1334                boxed_mouse_binding.unwrap().binding.get_device_descriptor(),
1335                input_device::InputDeviceDescriptor::Mouse(mouse_binding::MouseDeviceDescriptor {
1336                    device_id: 10,
1337                    absolute_x_range: None,
1338                    absolute_y_range: None,
1339                    wheel_v_range: None,
1340                    wheel_h_range: None,
1341                    buttons: Some(vec![0]),
1342                })
1343            );
1344        }
1345
1346        // Assert that inspect tree reflects new device discovered and connected.
1347        diagnostics_assertions::assert_data_tree!(inspector, root: {
1348            input_pipeline: {
1349                supported_input_devices: "Mouse",
1350                input_devices: {
1351                    devices_discovered: 1u64,
1352                    devices_connected: 1u64,
1353                    "instance_0_Mouse": contains {
1354                        reports_received_count: 0u64,
1355                        reports_filtered_count: 0u64,
1356                        events_generated: 0u64,
1357                        last_received_timestamp_ns: 0u64,
1358                        last_generated_timestamp_ns: 0u64,
1359                        "fuchsia.inspect.Health": {
1360                            status: "OK",
1361                            // Timestamp value is unpredictable and not relevant in this context,
1362                            // so we only assert that the property is present.
1363                            start_timestamp_nanos: AnyProperty
1364                        },
1365                    }
1366                }
1367            }
1368        });
1369    }
1370
1371    /// Tests that no device bindings are created because the input pipeline looks for keyboard devices
1372    /// but only a mouse exists.
1373    #[fuchsia::test]
1374    async fn watch_devices_no_matches_exist() {
1375        let dir = pseudo_directory! {
1376            "fuchsia.input.report.Service" => pseudo_directory! {
1377                "instance_0" => pseudo_directory! {
1378                    "input_device" => pseudo_fs_service::host(
1379                        move |mut request_stream: fidl_fuchsia_input_report::InputDeviceRequestStream| {
1380                            async move {
1381                                while let Some(input_device_request) =
1382                                    request_stream.try_next().await.unwrap()
1383                                {
1384                                    handle_input_device_request(input_device_request);
1385                                }
1386
1387                            }.boxed()
1388                        },
1389                    )
1390                }
1391            }
1392        };
1393
1394        // Get a proxy to the pseudo directory for the input pipeline. The input pipeline uses this
1395        // proxy to get connections to input devices.
1396        let svc_proxy =
1397            vfs::directory::serve_read_only(dir, vfs::execution_scope::ExecutionScope::new());
1398
1399        let (input_event_sender, _input_event_receiver) = futures::channel::mpsc::unbounded();
1400        let bindings: InputDeviceBindingMap = Arc::new(Mutex::new(SortedVecMap::new()));
1401        let supported_device_types = vec![input_device::InputDeviceType::Keyboard];
1402
1403        let inspector = fuchsia_inspect::Inspector::default();
1404        let test_node = inspector.root().create_child("input_pipeline");
1405        test_node.record_string(
1406            "supported_input_devices",
1407            supported_device_types.clone().iter().join(", "),
1408        );
1409        let input_devices = test_node.create_child("input_devices");
1410        // Assert that inspect tree is initialized with no devices.
1411        diagnostics_assertions::assert_data_tree!(inspector, root: {
1412            input_pipeline: {
1413                supported_input_devices: "Keyboard",
1414                input_devices: {}
1415            }
1416        });
1417
1418        let devices_discovered = input_devices.create_uint("devices_discovered", 0);
1419        let devices_connected = input_devices.create_uint("devices_connected", 0);
1420
1421        let dir = fuchsia_fs::directory::open_directory_async(
1422            &svc_proxy,
1423            "fuchsia.input.report.Service",
1424            fio::PERM_READABLE,
1425        )
1426        .expect("open service directory");
1427        let service = fuchsia_component::client::Service::from_service_dir_proxy(
1428            dir,
1429            fidl_fuchsia_input_report::ServiceMarker,
1430        );
1431
1432        let _ = InputPipeline::watch_for_devices(
1433            service,
1434            supported_device_types,
1435            input_event_sender,
1436            bindings.clone(),
1437            &input_devices,
1438            &devices_discovered,
1439            &devices_connected,
1440            true, /* break_on_idle */
1441            InputPipelineFeatureFlags { enable_merge_touch_events: false },
1442            metrics::MetricsLogger::default(),
1443            DeviceListenerRegistry::new(),
1444        )
1445        .await;
1446
1447        // Assert that no devices were found.
1448        {
1449            let bindings = bindings.lock();
1450            assert_eq!(bindings.len(), 0);
1451        }
1452
1453        // Assert that inspect tree reflects new device discovered, but not connected.
1454        diagnostics_assertions::assert_data_tree!(inspector, root: {
1455            input_pipeline: {
1456                supported_input_devices: "Keyboard",
1457                input_devices: {
1458                    devices_discovered: 1u64,
1459                    devices_connected: 0u64,
1460                    "instance_0_Unsupported": {
1461                        "fuchsia.inspect.Health": {
1462                            status: "UNHEALTHY",
1463                            message: "Unsupported device type.",
1464                            // Timestamp value is unpredictable and not relevant in this context,
1465                            // so we only assert that the property is present.
1466                            start_timestamp_nanos: AnyProperty
1467                        },
1468                    }
1469                }
1470            }
1471        });
1472    }
1473
1474    /// Tests that a single keyboard device binding is created for the input device registered
1475    /// through InputDeviceRegistry.
1476    #[fuchsia::test]
1477    async fn handle_input_device_registry_request_stream() {
1478        let (input_device_registry_proxy, input_device_registry_request_stream) =
1479            create_proxy_and_stream::<fidl_fuchsia_input_injection::InputDeviceRegistryMarker>();
1480        let (input_device_client_end, mut input_device_request_stream) =
1481            create_request_stream::<fidl_fuchsia_input_report::InputDeviceMarker>();
1482
1483        let device_types = vec![input_device::InputDeviceType::Mouse];
1484        let (input_event_sender, _input_event_receiver) = futures::channel::mpsc::unbounded();
1485        let bindings: InputDeviceBindingMap = Arc::new(Mutex::new(SortedVecMap::new()));
1486
1487        let device_listener_registry = DeviceListenerRegistry::new();
1488        let (listener_client, listener_server) =
1489            fidl_next::fuchsia::create_channel::<fidl_next_fuchsia_ui_input::DeviceListener>();
1490        let listener_client = listener_client.spawn();
1491        let _existing_devices = device_listener_registry.add_listener(listener_client);
1492
1493        let (event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1494        let _server_task = listener_server.spawn(MockDeviceListener { event_sender });
1495
1496        // Handle input device requests.
1497        let _task = fasync::Task::local(async move {
1498            // Register a device.
1499            let _ = input_device_registry_proxy.register(input_device_client_end);
1500
1501            let mut count = 0;
1502            while count < 2 {
1503                if let Some(input_device_request) =
1504                    input_device_request_stream.try_next().await.unwrap()
1505                {
1506                    handle_input_device_request(input_device_request);
1507                    count += 1;
1508                } else {
1509                    break;
1510                }
1511            }
1512
1513            // End handle_input_device_registry_request_stream() by taking the event stream.
1514            input_device_registry_proxy.take_event_stream();
1515        });
1516
1517        let inspector = fuchsia_inspect::Inspector::default();
1518        let test_node = inspector.root().create_child("input_pipeline");
1519
1520        // Start listening for InputDeviceRegistryRequests.
1521        let bindings_clone = bindings.clone();
1522        let device_listener_registry_clone = device_listener_registry.clone();
1523        let (task_sender, mut task_receiver) = futures::channel::mpsc::unbounded();
1524        let mut tasks = futures::stream::FuturesUnordered::new();
1525        let registry_fut = InputPipeline::handle_input_device_registry_request_stream(
1526            input_device_registry_request_stream,
1527            &device_types,
1528            &input_event_sender,
1529            &bindings_clone,
1530            &test_node,
1531            InputPipelineFeatureFlags { enable_merge_touch_events: false },
1532            metrics::MetricsLogger::default(),
1533            device_listener_registry_clone,
1534            task_sender,
1535        )
1536        .fuse();
1537
1538        let mut registry_fut = std::pin::pin!(registry_fut);
1539        loop {
1540            futures::select! {
1541                res = registry_fut => {
1542                    res.unwrap();
1543                    break;
1544                }
1545                task = task_receiver.next() => {
1546                    if let Some(task) = task {
1547                        tasks.push(task);
1548                    }
1549                }
1550                _ = tasks.select_next_some() => {}
1551            }
1552        }
1553
1554        // Assert that a device was registered.
1555        let device_id = {
1556            let bindings = bindings.lock();
1557            assert_eq!(bindings.len(), 1);
1558            *bindings.keys().next().unwrap()
1559        };
1560
1561        // Assert that device listener was notified.
1562        if let Some(event) = event_receiver.next().await {
1563            assert_eq!(event.action, Some(fidl_next_fuchsia_ui_input::Action::Added));
1564            assert_eq!(event.device_id, Some(device_id));
1565        } else {
1566            panic!("Expected a request on listener stream");
1567        }
1568    }
1569
1570    /// Tests that an injected device is removed and listeners are notified with Action::Removed
1571    /// when the injected device channel is closed.
1572    #[fasync::run_singlethreaded(test)]
1573    async fn handle_input_device_registry_disconnection() {
1574        let (input_device_registry_proxy, input_device_registry_request_stream) =
1575            create_proxy_and_stream::<fidl_fuchsia_input_injection::InputDeviceRegistryMarker>();
1576        let (input_device_client_end, mut input_device_request_stream) =
1577            create_request_stream::<fidl_fuchsia_input_report::InputDeviceMarker>();
1578
1579        let device_types = vec![input_device::InputDeviceType::Mouse];
1580        let (input_event_sender, _input_event_receiver) = futures::channel::mpsc::unbounded();
1581        let bindings: InputDeviceBindingMap = Arc::new(Mutex::new(SortedVecMap::new()));
1582
1583        let device_listener_registry = DeviceListenerRegistry::new();
1584        let (listener_client, listener_server) =
1585            fidl_next::fuchsia::create_channel::<fidl_next_fuchsia_ui_input::DeviceListener>();
1586        let listener_client = listener_client.spawn();
1587        let _existing_devices = device_listener_registry.add_listener(listener_client);
1588
1589        let (event_sender, mut event_receiver) = futures::channel::mpsc::unbounded();
1590        let _server_task = listener_server.spawn(MockDeviceListener { event_sender });
1591
1592        // Handle input device requests.
1593        let _task = fasync::Task::local(async move {
1594            // Register a device.
1595            let _ = input_device_registry_proxy.register(input_device_client_end);
1596
1597            let mut count = 0;
1598            while count < 2 {
1599                if let Some(input_device_request) =
1600                    input_device_request_stream.try_next().await.unwrap()
1601                {
1602                    handle_input_device_request(input_device_request);
1603                    count += 1;
1604                } else {
1605                    break;
1606                }
1607            }
1608
1609            // Close the device channel by dropping the request stream.
1610            std::mem::drop(input_device_request_stream);
1611
1612            // Wait a bit to let the local task run.
1613            fasync::Timer::new(fasync::MonotonicInstant::after(
1614                zx::MonotonicDuration::from_millis(100),
1615            ))
1616            .await;
1617
1618            // End handle_input_device_registry_request_stream() by taking the event stream.
1619            input_device_registry_proxy.take_event_stream();
1620        });
1621
1622        let inspector = fuchsia_inspect::Inspector::default();
1623        let test_node = inspector.root().create_child("input_pipeline");
1624
1625        // Start listening for InputDeviceRegistryRequests.
1626        let bindings_clone = bindings.clone();
1627        let device_listener_registry_clone = device_listener_registry.clone();
1628        let (task_sender, mut task_receiver) = futures::channel::mpsc::unbounded();
1629        let mut tasks = futures::stream::FuturesUnordered::new();
1630        let registry_fut = InputPipeline::handle_input_device_registry_request_stream(
1631            input_device_registry_request_stream,
1632            &device_types,
1633            &input_event_sender,
1634            &bindings_clone,
1635            &test_node,
1636            InputPipelineFeatureFlags { enable_merge_touch_events: false },
1637            metrics::MetricsLogger::default(),
1638            device_listener_registry_clone,
1639            task_sender,
1640        )
1641        .fuse();
1642
1643        let mut registry_fut = std::pin::pin!(registry_fut);
1644        loop {
1645            futures::select! {
1646                res = registry_fut => {
1647                    res.unwrap();
1648                    break;
1649                }
1650                task = task_receiver.next() => {
1651                    if let Some(task) = task {
1652                        tasks.push(task);
1653                    }
1654                }
1655                _ = tasks.select_next_some() => {}
1656            }
1657        }
1658        while let Some(_) = tasks.next().await {}
1659
1660        // Assert that the device was registered and then removed.
1661        {
1662            let bindings = bindings.lock();
1663            assert_eq!(bindings.len(), 0);
1664        }
1665
1666        // Assert that device listener was notified of Addition.
1667        let event_added = if let Some(event) = event_receiver.next().await {
1668            event
1669        } else {
1670            panic!("Expected Added event");
1671        };
1672        assert_eq!(event_added.action, Some(fidl_next_fuchsia_ui_input::Action::Added));
1673
1674        // Assert that device listener was notified of Removal.
1675        let event_removed = if let Some(event) = event_receiver.next().await {
1676            event
1677        } else {
1678            panic!("Expected Removed event");
1679        };
1680        assert_eq!(event_removed.action, Some(fidl_next_fuchsia_ui_input::Action::Removed));
1681    }
1682
1683    // Tests that correct properties are added to inspect node when InputPipeline is created.
1684    #[fuchsia::test]
1685    async fn check_inspect_node_has_correct_properties() {
1686        let device_types = vec![
1687            input_device::InputDeviceType::Touch,
1688            input_device::InputDeviceType::ConsumerControls,
1689        ];
1690        let inspector = fuchsia_inspect::Inspector::default();
1691        let test_node = inspector.root().create_child("input_pipeline");
1692        // Create fake input handler for assembly
1693        let (fake_handler_event_sender, _fake_handler_event_receiver) =
1694            futures::channel::mpsc::channel(100);
1695        let fake_input_handler =
1696            observe_fake_events_input_handler::ObserveFakeEventsInputHandler::new(
1697                fake_handler_event_sender,
1698            );
1699        let assembly = InputPipelineAssembly::new(metrics::MetricsLogger::default())
1700            .add_handler(fake_input_handler);
1701        let _test_input_pipeline = InputPipeline::new(
1702            &Incoming::new(),
1703            device_types,
1704            assembly,
1705            test_node,
1706            InputPipelineFeatureFlags { enable_merge_touch_events: false },
1707            metrics::MetricsLogger::default(),
1708        );
1709        diagnostics_assertions::assert_data_tree!(inspector, root: {
1710            input_pipeline: {
1711                supported_input_devices: "Touch, ConsumerControls",
1712                handlers_registered: 1u64,
1713                handlers_healthy: 1u64,
1714                input_devices: {}
1715            }
1716        });
1717    }
1718
1719    struct SpecificInterestFakeHandler {
1720        interest_types: Vec<input_device::InputEventType>,
1721        event_sender: std::cell::RefCell<futures::channel::mpsc::Sender<input_device::InputEvent>>,
1722    }
1723
1724    impl SpecificInterestFakeHandler {
1725        pub fn new(
1726            interest_types: Vec<input_device::InputEventType>,
1727            event_sender: futures::channel::mpsc::Sender<input_device::InputEvent>,
1728        ) -> Rc<Self> {
1729            Rc::new(SpecificInterestFakeHandler {
1730                interest_types,
1731                event_sender: std::cell::RefCell::new(event_sender),
1732            })
1733        }
1734    }
1735
1736    impl Handler for SpecificInterestFakeHandler {
1737        fn set_handler_healthy(self: std::rc::Rc<Self>) {}
1738        fn set_handler_unhealthy(self: std::rc::Rc<Self>, _msg: &str) {}
1739        fn get_name(&self) -> &'static str {
1740            "SpecificInterestFakeHandler"
1741        }
1742
1743        fn interest(&self) -> Vec<input_device::InputEventType> {
1744            self.interest_types.clone()
1745        }
1746    }
1747
1748    #[async_trait(?Send)]
1749    impl input_handler::InputHandler for SpecificInterestFakeHandler {
1750        async fn handle_input_event(
1751            self: Rc<Self>,
1752            input_event: input_device::InputEvent,
1753        ) -> Vec<input_device::InputEvent> {
1754            match self.event_sender.borrow_mut().try_send(input_event.clone()) {
1755                Err(e) => panic!("SpecificInterestFakeHandler failed to send event: {:?}", e),
1756                Ok(_) => {}
1757            }
1758            vec![input_event]
1759        }
1760    }
1761
1762    #[fuchsia::test]
1763    async fn run_only_sends_events_to_interested_handlers() {
1764        // Mouse Handler (Specific Interest: Mouse)
1765        let (mouse_sender, mut mouse_receiver) = futures::channel::mpsc::channel(1);
1766        let mouse_handler =
1767            SpecificInterestFakeHandler::new(vec![InputEventType::Mouse], mouse_sender);
1768
1769        // Fake Handler (Specific Interest: Fake)
1770        let (fake_sender, mut fake_receiver) = futures::channel::mpsc::channel(1);
1771        let fake_handler =
1772            SpecificInterestFakeHandler::new(vec![InputEventType::Fake], fake_sender);
1773
1774        let (pipeline_sender, pipeline_receiver, handlers, _, _, _) =
1775            InputPipelineAssembly::new(metrics::MetricsLogger::default())
1776                .add_handler(mouse_handler)
1777                .add_handler(fake_handler)
1778                .into_components();
1779
1780        // Run the pipeline logic
1781        let _runner_task = fasync::Task::local(InputPipeline::run(
1782            pipeline_receiver,
1783            handlers,
1784            metrics::MetricsLogger::default(),
1785        ));
1786
1787        // Create a Fake event
1788        let fake_event = input_device::InputEvent {
1789            device_event: input_device::InputDeviceEvent::Fake,
1790            device_descriptor: input_device::InputDeviceDescriptor::Fake,
1791            event_time: zx::MonotonicInstant::get(),
1792            handled: input_device::Handled::No,
1793            trace_id: None,
1794        };
1795
1796        // Send the Fake event
1797        pipeline_sender.unbounded_send(vec![fake_event.clone()]).expect("failed to send event");
1798
1799        // Verify Fake Handler received it
1800        let received_by_fake = fake_receiver.next().await;
1801        assert_eq!(received_by_fake, Some(fake_event));
1802
1803        // Verify Mouse Handler did NOT receive it
1804        assert!(mouse_receiver.try_next().is_err());
1805    }
1806
1807    fn create_mouse_event(x: f32, y: f32) -> input_device::InputEvent {
1808        input_device::InputEvent {
1809            device_event: input_device::InputDeviceEvent::Mouse(mouse_binding::MouseEvent::new(
1810                mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1811                    counts: Position { x, y },
1812                }),
1813                None,
1814                None,
1815                mouse_binding::MousePhase::Move,
1816                SortedVecSet::new(),
1817                SortedVecSet::new(),
1818                None,
1819                None,
1820            )),
1821            device_descriptor: input_device::InputDeviceDescriptor::Mouse(
1822                mouse_binding::MouseDeviceDescriptor {
1823                    device_id: 1,
1824                    absolute_x_range: None,
1825                    absolute_y_range: None,
1826                    wheel_v_range: None,
1827                    wheel_h_range: None,
1828                    buttons: None,
1829                },
1830            ),
1831            event_time: zx::MonotonicInstant::get(),
1832            handled: input_device::Handled::No,
1833            trace_id: None,
1834        }
1835    }
1836
1837    #[fuchsia::test]
1838    async fn run_mixed_event_types_dispatched_correctly() {
1839        // Mouse Handler (Specific Interest: Mouse)
1840        let (mouse_sender, mut mouse_receiver) = futures::channel::mpsc::channel(10);
1841        let mouse_handler =
1842            SpecificInterestFakeHandler::new(vec![InputEventType::Mouse], mouse_sender);
1843
1844        // Fake Handler (Specific Interest: Fake)
1845        let (fake_sender, mut fake_receiver) = futures::channel::mpsc::channel(10);
1846        let fake_handler =
1847            SpecificInterestFakeHandler::new(vec![InputEventType::Fake], fake_sender);
1848
1849        let (pipeline_sender, pipeline_receiver, handlers, _, _, _) =
1850            InputPipelineAssembly::new(metrics::MetricsLogger::default())
1851                .add_handler(mouse_handler)
1852                .add_handler(fake_handler)
1853                .into_components();
1854
1855        // Run the pipeline logic
1856        let _runner_task = fasync::Task::local(InputPipeline::run(
1857            pipeline_receiver,
1858            handlers,
1859            metrics::MetricsLogger::default(),
1860        ));
1861
1862        // Create events
1863        let mouse_event_1 = create_mouse_event(1.0, 1.0);
1864        let mouse_event_2 = create_mouse_event(2.0, 2.0);
1865        let mouse_event_3 = create_mouse_event(3.0, 3.0);
1866
1867        let fake_event_1 = input_device::InputEvent {
1868            device_event: input_device::InputDeviceEvent::Fake,
1869            device_descriptor: input_device::InputDeviceDescriptor::Fake,
1870            event_time: zx::MonotonicInstant::get(),
1871            handled: input_device::Handled::No,
1872            trace_id: None,
1873        };
1874
1875        // Send mixed batch: [Mouse, Mouse, Fake, Mouse]
1876        // This should result in 3 chunks: [Mouse, Mouse], [Fake], [Mouse]
1877        let mixed_batch = vec![
1878            mouse_event_1.clone(),
1879            mouse_event_2.clone(),
1880            fake_event_1.clone(),
1881            mouse_event_3.clone(),
1882        ];
1883        pipeline_sender.unbounded_send(mixed_batch).expect("failed to send events");
1884
1885        // Verify Mouse Handler received M1, M2, and then M3
1886        assert_eq!(mouse_receiver.next().await, Some(mouse_event_1));
1887        assert_eq!(mouse_receiver.next().await, Some(mouse_event_2));
1888        assert_eq!(mouse_receiver.next().await, Some(mouse_event_3));
1889
1890        // Verify Fake Handler received F1
1891        assert_eq!(fake_receiver.next().await, Some(fake_event_1));
1892    }
1893}