Skip to main content

starnix_modules_input/
input_event_relay.rs

1// Copyright 2024 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::{InputDeviceStatus, InputFile, uinput};
6use fidl::endpoints::{ClientEnd, RequestStream};
7use fidl_fuchsia_ui_input::TouchDeviceInfo;
8use fidl_fuchsia_ui_input3::{
9    KeyEventStatus, KeyboardListenerMarker, KeyboardListenerRequest, KeyboardListenerRequestStream,
10    KeyboardSynchronousProxy,
11};
12use fidl_fuchsia_ui_pointer::{
13    MouseEvent as FidlMouseEvent, TouchEvent as FidlTouchEvent, TouchPointerSample,
14    {self as fuipointer},
15};
16use fidl_fuchsia_ui_policy as fuipolicy;
17use fidl_fuchsia_ui_views as fuiviews;
18use futures::StreamExt as _;
19use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
20use futures::channel::oneshot::{self, Sender};
21use futures::executor::block_on;
22use sorted_vec_map::SortedVecMap;
23use starnix_core::power::{ContainerWakingStream, create_proxy_for_wake_events_counter};
24use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
25use starnix_core::task::{CurrentTask, Kernel};
26use starnix_logging::log_warn;
27use starnix_modules_input_event_conversion::button_fuchsia_to_linux::{
28    new_touch_buttons_bitvec, parse_fidl_media_button_event, parse_fidl_touch_button_event,
29};
30use starnix_modules_input_event_conversion::key_fuchsia_to_linux::parse_fidl_keyboard_event_to_linux_input_event;
31use starnix_modules_input_event_conversion::mouse_fuchsia_to_linux::FuchsiaMouseEventToLinuxMouseEventConverter;
32use starnix_modules_input_event_conversion::touch_fuchsia_to_linux::FuchsiaTouchEventToLinuxTouchEventConverter;
33use starnix_sync::{InputEventRelayOpenedFilesLock, LockDepMutex};
34use starnix_uapi::uapi;
35use std::collections::VecDeque;
36use std::sync::{Arc, Weak};
37
38const INPUT_RELAY_ROLE_NAME: &str = "fuchsia.starnix.kthread.input_relay";
39
40#[derive(Clone, Copy)]
41pub enum EventProxyMode {
42    /// Don't proxy input events at all.
43    None,
44
45    /// Have the Starnix runner proxy events such that the container
46    /// will wake up if events are received while the container is
47    /// suspended.
48    WakeContainer,
49}
50
51#[derive(Default)]
52pub struct OpenedFilesState {
53    files: Vec<Weak<InputFile>>,
54    has_been_opened: bool,
55    buffered_events: Vec<uapi::input_event>,
56}
57
58impl OpenedFilesState {
59    pub fn on_file_opened(&mut self, file: &Arc<InputFile>) {
60        if !self.has_been_opened {
61            self.has_been_opened = true;
62            if !self.buffered_events.is_empty() {
63                file.add_events(std::mem::take(&mut self.buffered_events));
64            }
65        }
66        self.files.push(Arc::downgrade(file));
67    }
68}
69
70impl std::ops::Deref for OpenedFilesState {
71    type Target = Vec<Weak<InputFile>>;
72    fn deref(&self) -> &Self::Target {
73        &self.files
74    }
75}
76
77impl std::ops::DerefMut for OpenedFilesState {
78    fn deref_mut(&mut self) -> &mut Self::Target {
79        &mut self.files
80    }
81}
82
83pub type OpenedFiles = Arc<LockDepMutex<OpenedFilesState, InputEventRelayOpenedFilesLock>>;
84
85pub enum InputDeviceType {
86    Touch(FuchsiaTouchEventToLinuxTouchEventConverter),
87    Keyboard,
88    Mouse(FuchsiaMouseEventToLinuxMouseEventConverter),
89}
90
91impl std::fmt::Display for InputDeviceType {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        match self {
94            InputDeviceType::Touch(_) => write!(f, "touch"),
95            InputDeviceType::Keyboard => write!(f, "keyboard"),
96            InputDeviceType::Mouse(_) => write!(f, "mouse"),
97        }
98    }
99}
100
101enum DeviceRegistration {
102    Pending { kernel: Arc<Kernel>, device: crate::InputDevice, device_id: DeviceId },
103    Registered,
104    Failed,
105}
106
107impl DeviceRegistration {
108    /// Creates a `Pending` registration for `device`, which will be registered with `kernel`
109    /// under `device_id` on the first call to `ensure_registered`.
110    ///
111    /// `device` must not already be registered: `DeviceRegistry::register_device` silently
112    /// overwrites an existing minor device entry, so a double registration would otherwise only
113    /// surface as a warning log.
114    fn pending(kernel: Arc<Kernel>, device: crate::InputDevice, device_id: DeviceId) -> Self {
115        debug_assert!(
116            {
117                let devt = starnix_uapi::device_id::DeviceId::new(
118                    starnix_uapi::device_id::INPUT_MAJOR,
119                    device_id,
120                );
121                let next_devt = starnix_uapi::device_id::DeviceId::new(
122                    starnix_uapi::device_id::INPUT_MAJOR,
123                    device_id + 1,
124                );
125                kernel
126                    .device_registry
127                    .list_minor_devices(starnix_core::device::DeviceMode::Char, devt..next_devt)
128                    .is_empty()
129            },
130            "input device {device_id} must not be registered before lazy registration",
131        );
132        Self::Pending { kernel, device, device_id }
133    }
134
135    fn ensure_registered(&mut self) {
136        let Self::Pending { kernel, device, device_id } = self else { return };
137        match device.clone().register(kernel, *device_id) {
138            Ok(()) => *self = Self::Registered,
139            Err(e) => {
140                log_warn!("unable to register input device {device_id:?}: {e:?}");
141                // Intentionally abandon registering the mouse device after one failed attempt
142                // rather than retrying and logging on every subsequent input event.
143                *self = Self::Failed;
144            }
145        }
146    }
147}
148
149pub struct DeviceState {
150    device_type: InputDeviceType,
151    open_files: OpenedFiles,
152    inspect_status: Option<Arc<InputDeviceStatus>>,
153    registration: DeviceRegistration,
154}
155
156pub struct TrackedWakeLease {
157    _lease: fidl::EventPair,
158    device_status: Arc<InputDeviceStatus>,
159}
160
161impl TrackedWakeLease {
162    pub fn new(lease: fidl::EventPair, device_status: Arc<InputDeviceStatus>) -> Self {
163        device_status.increment_active_wake_leases(1);
164        device_status.count_events_with_wake_lease(1);
165        Self { _lease: lease, device_status }
166    }
167}
168
169impl Drop for TrackedWakeLease {
170    fn drop(&mut self) {
171        self.device_status.decrement_active_wake_leases(1);
172    }
173}
174
175pub type DeviceId = u32;
176
177pub const DEFAULT_TOUCH_DEVICE_ID: DeviceId = 0;
178pub const DEFAULT_KEYBOARD_DEVICE_ID: DeviceId = 1;
179pub const DEFAULT_MOUSE_DEVICE_ID: DeviceId = 2;
180
181enum DeviceStateChange {
182    Add(DeviceId, DeviceState, Sender<()>),
183    Remove(DeviceId, Sender<()>),
184}
185
186pub fn new_input_relay() -> (InputEventsRelay, Arc<InputEventsRelayHandle>) {
187    let (sender, receiver) = unbounded();
188
189    (
190        InputEventsRelay { devices: SortedVecMap::new(), receiver },
191        Arc::new(InputEventsRelayHandle { sender }),
192    )
193}
194
195pub struct InputEventsRelayHandle {
196    sender: UnboundedSender<DeviceStateChange>,
197}
198
199impl InputEventsRelayHandle {
200    pub fn add_touch_device(
201        self: &Arc<Self>,
202        device_id: DeviceId,
203        open_files: OpenedFiles,
204        inspect_status: Option<Arc<InputDeviceStatus>>,
205    ) {
206        let (sender, receiver) = oneshot::channel();
207        let _ = self.sender.unbounded_send(DeviceStateChange::Add(
208            device_id,
209            DeviceState {
210                device_type: InputDeviceType::Touch(
211                    FuchsiaTouchEventToLinuxTouchEventConverter::create(),
212                ),
213                open_files,
214                inspect_status,
215                registration: DeviceRegistration::Registered,
216            },
217            sender,
218        ));
219        let _ = block_on(receiver);
220    }
221
222    pub fn add_keyboard_device(
223        &self,
224        device_id: DeviceId,
225        open_files: OpenedFiles,
226        inspect_status: Option<Arc<InputDeviceStatus>>,
227    ) {
228        let (sender, receiver) = oneshot::channel();
229        let _ = self.sender.unbounded_send(DeviceStateChange::Add(
230            device_id,
231            DeviceState {
232                device_type: InputDeviceType::Keyboard,
233                open_files,
234                inspect_status,
235                registration: DeviceRegistration::Registered,
236            },
237            sender,
238        ));
239        let _ = block_on(receiver);
240    }
241
242    pub fn add_mouse_device(
243        &self,
244        device_id: DeviceId,
245        open_files: OpenedFiles,
246        inspect_status: Option<Arc<InputDeviceStatus>>,
247    ) {
248        let (sender, receiver) = oneshot::channel();
249        let _ = self.sender.unbounded_send(DeviceStateChange::Add(
250            device_id,
251            DeviceState {
252                device_type: InputDeviceType::Mouse(
253                    FuchsiaMouseEventToLinuxMouseEventConverter::create(),
254                ),
255                open_files,
256                inspect_status,
257                registration: DeviceRegistration::Registered,
258            },
259            sender,
260        ));
261        let _ = block_on(receiver);
262    }
263
264    pub fn remove_device(&self, device_id: DeviceId) {
265        let (sender, receiver) = oneshot::channel();
266        let _ = self.sender.unbounded_send(DeviceStateChange::Remove(device_id, sender));
267        let _ = block_on(receiver);
268    }
269}
270
271pub struct InputEventsRelay {
272    devices: SortedVecMap<DeviceId, DeviceState>,
273    receiver: UnboundedReceiver<DeviceStateChange>,
274}
275
276impl InputEventsRelay {
277    // TODO(https://fxbug.dev/371602479): Use `fuchsia.ui.SupportedInputDevices` to create
278    // relays.
279    // start_relays will take over the ownership of InputEventsRelay.
280    // If `default_mouse_device` is `Some`, it must be an unregistered `InputDevice`; the relay
281    // will lazily register it with `kernel` on the first converted mouse event.
282    pub fn start_relays(
283        mut self: Self,
284        kernel: &Kernel,
285        event_proxy_mode: EventProxyMode,
286        touch_source_client_end: ClientEnd<fuipointer::TouchSourceV2Marker>,
287        keyboard: KeyboardSynchronousProxy,
288        mouse_source_client_end: ClientEnd<fuipointer::MouseSourceV2Marker>,
289        view_ref: fuiviews::ViewRef,
290        registry_proxy: fuipolicy::DeviceListenerRegistrySynchronousProxy,
291        default_touch_device_opened_files: OpenedFiles,
292        default_keyboard_device_opened_files: OpenedFiles,
293        default_mouse_device: Option<crate::InputDevice>,
294        default_touch_device_inspect: Option<Arc<InputDeviceStatus>>,
295        default_keyboard_device_inspect: Option<Arc<InputDeviceStatus>>,
296    ) {
297        let f = async move |current_task: &CurrentTask| {
298            let kernel = current_task.kernel();
299            // touch
300            let (mut default_touch_device, touch_source_proxy, mut touch_waking_stream) =
301                setup_touch_relay(
302                    kernel,
303                    event_proxy_mode,
304                    touch_source_client_end,
305                    default_touch_device_opened_files,
306                    default_touch_device_inspect,
307                );
308            let mut touch_future = touch_waking_stream.next();
309
310            // mouse
311            let (mut default_mouse_device, mouse_source_proxy, mut mouse_waking_stream) =
312                setup_mouse_relay(
313                    kernel,
314                    event_proxy_mode,
315                    mouse_source_client_end,
316                    default_mouse_device,
317                );
318            let mut mouse_future = mouse_waking_stream.next();
319
320            // keyboard
321            // `_keyboard_proxy` is load-bearing despite being unused: it holds the channel to
322            // text_manager open for the lifetime of the relay loop below. Dropping it closes
323            // the channel, which deregisters our `KeyboardListener` and silently stops all
324            // key delivery. Do not remove it as an unused binding.
325            let (mut default_keyboard_device, mut keyboard_event_stream, _keyboard_proxy) =
326                setup_keyboard_relay(
327                    keyboard,
328                    view_ref,
329                    default_keyboard_device_opened_files.clone(),
330                    default_keyboard_device_inspect.clone(),
331                );
332
333            // button
334            let (
335                mut default_button_device,
336                mut media_buttons_waking_stream,
337                mut touch_buttons_waking_stream,
338            ) = setup_button_relay(
339                kernel,
340                registry_proxy,
341                event_proxy_mode,
342                default_keyboard_device_opened_files,
343                default_keyboard_device_inspect,
344            );
345            let mut media_buttons_future = media_buttons_waking_stream.next();
346            let mut touch_buttons_future = touch_buttons_waking_stream.next();
347
348            let mut power_was_pressed = false;
349            let mut function_was_pressed = false;
350            let mut volume_up_was_pressed = false;
351            let mut volume_down_was_pressed = false;
352            let mut touch_buttons_were_pressed = new_touch_buttons_bitvec();
353
354            loop {
355                futures::select! {
356                    touch_res = touch_future => {
357                        match touch_res {
358                            Some(Ok(fuipointer::TouchSourceV2Event::OnTouchEvents {
359                                events,
360                                last_event_stamp,
361                            })) => {
362                                self.process_touch_event(
363                                    &mut default_touch_device,
364                                    events,
365                                );
366                                if let Err(e) = touch_source_proxy.acknowledge_events(last_event_stamp) {
367                                    log_warn!("error acknowledging touch events: {:?}", e);
368                                }
369                                touch_future = touch_waking_stream.next();
370                            }
371                            Some(Ok(fuipointer::TouchSourceV2Event::_UnknownEvent { ordinal, .. })) => {
372                                log_warn!("unknown event on TouchSourceV2: {}", ordinal);
373                                touch_future = touch_waking_stream.next();
374                            }
375                            Some(Err(e)) => {
376                                log_warn!(
377                                    "error {:?} reading from TouchSourceV2Proxy; input is stopped",
378                                    e
379                                );
380                            }
381                            None => {}
382                        }
383                    }
384                    mouse_res = mouse_future => {
385                        match mouse_res {
386                            Some(Ok(fuipointer::MouseSourceV2Event::OnMouseEvents {
387                                events,
388                                last_event_stamp,
389                            })) => {
390                                self.process_mouse_event(&mut default_mouse_device, events);
391                                if let Err(e) = mouse_source_proxy.acknowledge_events(last_event_stamp) {
392                                    log_warn!("error acknowledging mouse events: {:?}", e);
393                                }
394                                mouse_future = mouse_waking_stream.next();
395                            }
396                            Some(Ok(fuipointer::MouseSourceV2Event::_UnknownEvent { ordinal, .. })) => {
397                                log_warn!("unknown event on MouseSourceV2: {}", ordinal);
398                                mouse_future = mouse_waking_stream.next();
399                            }
400                            Some(Err(e)) => {
401                                log_warn!(
402                                    "error {:?} reading from MouseSourceV2Proxy; input is stopped",
403                                    e
404                                );
405                            }
406                            None => {}
407                        }
408                    }
409                    media_buttons_res = media_buttons_future => {
410                        match media_buttons_res {
411                            Some(Ok(event)) => {
412                                (
413                                    power_was_pressed,
414                                    function_was_pressed,
415                                    volume_up_was_pressed,
416                                    volume_down_was_pressed,
417                                ) = self.process_media_button_event(
418                                    &mut default_button_device,
419                                    event,
420                                    power_was_pressed,
421                                    function_was_pressed,
422                                    volume_up_was_pressed,
423                                    volume_down_was_pressed,
424                                );
425                                media_buttons_future = media_buttons_waking_stream.next();
426                            }
427                            _ => {}
428                        }
429                    }
430                    touch_buttons_res = touch_buttons_future => {
431                        match touch_buttons_res {
432                            Some(Ok(event)) => {
433                                touch_buttons_were_pressed = self.process_touch_button_event(
434                                    &mut default_touch_device,
435                                    event,
436                                    &touch_buttons_were_pressed,
437                                );
438                                touch_buttons_future = touch_buttons_waking_stream.next();
439                            }
440                            _ => {}
441                        }
442                    }
443                    e = keyboard_event_stream.next() => {
444                        match e  {
445                            Some(Ok(request)) => {
446                                self.process_keyboard(&mut default_keyboard_device, request);
447                            }
448                            _ => {}
449                        }
450                    }
451                    e = self.receiver.next() => {
452                        match e {
453                            Some(event) => {
454                                match event {
455                                    DeviceStateChange::Add(id, device_state, sender) => {
456                                        self.devices.insert(id, device_state);
457                                        let _ = sender.send(());
458                                    }
459                                    DeviceStateChange::Remove(id, sender) => {
460                                        self.devices.remove(&id);
461                                        let _ = sender.send(());
462                                    }
463                                }
464                            }
465                            _ => {}
466                        }
467                    }
468                    complete => break,
469                }
470            }
471        };
472        let req = SpawnRequestBuilder::new()
473            .with_debug_name("input-event-relay")
474            .with_role(INPUT_RELAY_ROLE_NAME)
475            .with_async_closure(f)
476            .build();
477        kernel.kthreads.spawner().spawn_from_request(req);
478    }
479
480    fn process_touch_event(
481        self: &mut Self,
482        default_touch_device: &mut DeviceState,
483        touch_events: Vec<FidlTouchEvent>,
484    ) {
485        fuchsia_trace::duration!("input", "starnix_process_touch_event");
486        for e in &touch_events {
487            match e.trace_flow_id {
488                Some(trace_flow_id) => {
489                    fuchsia_trace::flow_end!(
490                        "input",
491                        "dispatch_event_to_client",
492                        trace_flow_id.into()
493                    );
494                }
495                None => {
496                    log_warn!("touch event has not tracing id");
497                }
498            }
499        }
500        let num_received_events: u64 = touch_events.len().try_into().unwrap();
501
502        let mut num_ignored_events: u64 = 0;
503
504        // 1 vec may contains events from different device.
505        let (events_by_device, ignored_events) = group_touch_events_by_device_id(touch_events);
506        num_ignored_events += ignored_events;
507
508        for (device_id, mut events) in events_by_device {
509            fuchsia_trace::duration_begin!("input", "starnix_process_per_device_touch_event");
510
511            let dev = self.devices.get_mut(&device_id).unwrap_or(default_touch_device);
512
513            let mut num_converted_events: u64 = 0;
514            let mut num_unexpected_events: u64 = 0;
515            let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
516
517            #[allow(clippy::collection_is_never_read)]
518            let mut tracked_leases = vec![];
519            for event in &mut events {
520                if let Some(lease) = event.wake_lease.take() {
521                    if let Some(status) = &dev.inspect_status {
522                        tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
523                    }
524                }
525            }
526
527            let last_event_time_ns: i64;
528            if let InputDeviceType::Touch(ref mut converter) = dev.device_type {
529                let mut batch = converter.handle(events);
530                new_events.append(&mut batch.events);
531                num_converted_events += batch.count_converted_fidl_events;
532                num_ignored_events += batch.count_ignored_fidl_events;
533                num_unexpected_events += batch.count_unexpected_fidl_events;
534                last_event_time_ns = batch.last_event_time_ns;
535            } else {
536                fuchsia_trace::duration_end!("input", "starnix_process_per_device_touch_event");
537                log_warn!(
538                    "Non touch device received touch events: device_id = {}, device_type = {}",
539                    device_id,
540                    dev.device_type
541                );
542                continue;
543            }
544
545            if let Some(dev_inspect_status) = &dev.inspect_status {
546                dev_inspect_status.count_total_received_events(num_received_events);
547                dev_inspect_status.count_total_ignored_events(num_ignored_events);
548                dev_inspect_status.count_total_unexpected_events(num_unexpected_events);
549                dev_inspect_status.count_total_converted_events(num_converted_events);
550                dev_inspect_status.count_total_generated_events(
551                    new_events.len().try_into().unwrap(),
552                    last_event_time_ns,
553                );
554            } else {
555                log_warn!(
556                    "unable to record inspect for device_id: {}, device_type: {}",
557                    device_id,
558                    dev.device_type
559                );
560            }
561
562            fuchsia_trace::duration_end!("input", "starnix_process_per_device_touch_event");
563            dev.open_files.lock().retain(|f| {
564                let Some(file) = f.upgrade() else {
565                    log_warn!("Dropping input file for touch that failed to upgrade");
566                    return false;
567                };
568                match &file.inspect_status {
569                    Some(file_inspect_status) => {
570                        file_inspect_status.count_received_events(num_received_events);
571                        file_inspect_status.count_ignored_events(num_ignored_events);
572                        file_inspect_status.count_unexpected_events(num_unexpected_events);
573                        file_inspect_status.count_converted_events(num_converted_events);
574                    }
575                    None => {
576                        log_warn!("unable to record inspect within the input file")
577                    }
578                }
579                if !new_events.is_empty() {
580                    // TODO(https://fxbug.dev/42075438): Reading from an `InputFile` should
581                    // not provide access to events that occurred before the file was
582                    // opened.
583                    if let Some(file_inspect_status) = &file.inspect_status {
584                        file_inspect_status.count_generated_events(
585                            new_events.len().try_into().unwrap(),
586                            last_event_time_ns,
587                        );
588                    }
589                    file.add_events(new_events.clone().into_iter().collect());
590                }
591
592                true
593            });
594        }
595    }
596
597    fn process_keyboard(
598        self: &mut Self,
599        default_keyboard_device: &mut DeviceState,
600        request: KeyboardListenerRequest,
601    ) {
602        match request {
603            KeyboardListenerRequest::OnKeyEvent { event, responder } => {
604                fuchsia_trace::duration!("input", "starnix_process_keyboard_event");
605
606                let new_events = parse_fidl_keyboard_event_to_linux_input_event(
607                    &event,
608                    uinput::uinput_running(),
609                );
610
611                let dev = match event.device_id {
612                    Some(device_id) => {
613                        self.devices.get_mut(&device_id).unwrap_or(default_keyboard_device)
614                    }
615                    None => default_keyboard_device,
616                };
617
618                // These counters are denominated in FIDL events, not uapi events: the
619                // documented invariant is received = ignored + unexpected + converted (see
620                // `InputDeviceStatus`). One FIDL key event converts to several uapi events
621                // (the key itself plus a SYN), so only the *generated* counters take
622                // `new_events.len()`.
623                let (converted_events, ignored_events, generated_events) = match new_events.len() {
624                    0 => (0u64, 1u64, 0u64),
625                    len => (1u64, 0u64, len as u64),
626                };
627                let last_time = event.timestamp.unwrap_or(0);
628
629                if let Some(dev_inspect_status) = &dev.inspect_status {
630                    dev_inspect_status.count_total_received_events(1);
631                    dev_inspect_status.count_total_ignored_events(ignored_events);
632                    dev_inspect_status.count_total_converted_events(converted_events);
633                    // Guarded because `count_total_generated_events` *stores* the timestamp:
634                    // calling it with a count of 0 would move
635                    // `last_generated_uapi_event_timestamp_ns` on an event that generated
636                    // nothing.
637                    if generated_events > 0 {
638                        dev_inspect_status
639                            .count_total_generated_events(generated_events, last_time);
640                    }
641                } else {
642                    log_warn!("unable to record inspect for keyboard device");
643                }
644
645                dev.open_files.lock().retain(|f| {
646                    let Some(file) = f.upgrade() else {
647                        log_warn!("Dropping input file for keyboard that failed to upgrade");
648                        return false;
649                    };
650                    match &file.inspect_status {
651                        Some(file_inspect_status) => {
652                            file_inspect_status.count_received_events(1);
653                            file_inspect_status.count_ignored_events(ignored_events);
654                            file_inspect_status.count_converted_events(converted_events);
655                            if generated_events > 0 {
656                                file_inspect_status
657                                    .count_generated_events(generated_events, last_time);
658                            }
659                        }
660                        None => {
661                            log_warn!("unable to record inspect within the input file")
662                        }
663                    }
664                    if !new_events.is_empty() {
665                        file.add_events(new_events.clone().into_iter().collect());
666                    }
667
668                    true
669                });
670
671                responder.send(KeyEventStatus::Handled).expect("");
672            }
673        }
674    }
675
676    fn process_media_button_event(
677        &mut self,
678        default_button_device: &mut DeviceState,
679        button_event: fuipolicy::MediaButtonsListenerRequest,
680        power_was_pressed: bool,
681        function_was_pressed: bool,
682        volume_up_was_pressed: bool,
683        volume_down_was_pressed: bool,
684    ) -> (bool, bool, bool, bool) {
685        let mut power_was_pressed_after = false;
686        let mut function_was_pressed_after = false;
687        let mut volume_up_was_pressed_after = false;
688        let mut volume_down_was_pressed_after = false;
689        match button_event {
690            fuipolicy::MediaButtonsListenerRequest::OnEvent { mut event, responder } => {
691                if let Some(trace_flow_id) = event.trace_flow_id {
692                    fuchsia_trace::flow_end!(
693                        "input",
694                        "dispatch_media_buttons_to_listeners",
695                        trace_flow_id.into()
696                    );
697                }
698                fuchsia_trace::duration!("input", "starnix_process_media_button_event");
699
700                let batch = parse_fidl_media_button_event(
701                    &event,
702                    power_was_pressed,
703                    function_was_pressed,
704                    volume_up_was_pressed,
705                    volume_down_was_pressed,
706                );
707
708                power_was_pressed_after = batch.power_is_pressed;
709                function_was_pressed_after = batch.function_is_pressed;
710                volume_up_was_pressed_after = batch.volume_up_is_pressed;
711                volume_down_was_pressed_after = batch.volume_down_is_pressed;
712
713                let (converted_events, ignored_events, generated_events) = match batch.events.len()
714                {
715                    0 => (0u64, 1u64, 0u64),
716                    len => {
717                        if len % 2 == 1 {
718                            log_warn!(
719                                "unexpectedly received {} events: there should always be an even number of non-empty events.",
720                                len
721                            );
722                        }
723                        (1u64, 0u64, len as u64)
724                    }
725                };
726
727                let dev = match event.device_id {
728                    Some(device_id) => {
729                        self.devices.get_mut(&device_id).unwrap_or(default_button_device)
730                    }
731                    None => default_button_device,
732                };
733
734                #[allow(clippy::collection_is_never_read)]
735                let mut tracked_leases = vec![];
736                if let Some(lease) = event.wake_lease.take() {
737                    if let Some(status) = &dev.inspect_status {
738                        tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
739                    }
740                }
741
742                if let Some(dev_inspect_status) = &dev.inspect_status {
743                    dev_inspect_status.count_total_received_events(1);
744                    dev_inspect_status.count_total_ignored_events(ignored_events);
745                    dev_inspect_status.count_total_converted_events(converted_events);
746                    dev_inspect_status.count_total_generated_events(
747                        generated_events,
748                        batch.event_time.into_nanos().try_into().unwrap(),
749                    );
750                } else {
751                    log_warn!("unable to record inspect for button device");
752                }
753
754                dev.open_files.lock().retain(|f| {
755                    let Some(file) = f.upgrade() else {
756                        log_warn!("Dropping input file for buttons that failed to upgrade");
757                        return false;
758                    };
759                    match &file.inspect_status {
760                        Some(file_inspect_status) => {
761                            file_inspect_status.count_received_events(1);
762                            file_inspect_status.count_ignored_events(ignored_events);
763                            file_inspect_status.count_converted_events(converted_events);
764                        }
765                        None => {
766                            log_warn!("unable to record inspect within the input file")
767                        }
768                    }
769                    if !batch.events.is_empty() {
770                        if let Some(file_inspect_status) = &file.inspect_status {
771                            file_inspect_status.count_generated_events(
772                                generated_events,
773                                batch.event_time.into_nanos().try_into().unwrap(),
774                            );
775                        }
776                        file.add_events(batch.events.clone());
777                    }
778
779                    true
780                });
781
782                responder.send().expect("media buttons responder failed to respond");
783            }
784            _ => { /* Ignore deprecated OnMediaButtonsEvent */ }
785        }
786
787        (
788            power_was_pressed_after,
789            function_was_pressed_after,
790            volume_up_was_pressed_after,
791            volume_down_was_pressed_after,
792        )
793    }
794
795    fn process_touch_button_event(
796        &mut self,
797        default_touch_device: &mut DeviceState,
798        button_event: fuipolicy::TouchButtonsListenerRequest,
799        touch_buttons_were_pressed: &bit_vec::BitVec,
800    ) -> bit_vec::BitVec {
801        fuchsia_trace::duration!("input", "starnix_process_touch_button_event");
802        match button_event {
803            fuipolicy::TouchButtonsListenerRequest::OnEvent { mut event, responder } => {
804                if let Some(trace_flow_id) = event.trace_flow_id {
805                    fuchsia_trace::flow_end!(
806                        "input",
807                        "dispatch_touch_button_to_listeners",
808                        trace_flow_id.into()
809                    );
810                }
811                let batch = parse_fidl_touch_button_event(&event, touch_buttons_were_pressed);
812
813                let (converted_events, ignored_events, generated_events) = match batch.events.len()
814                {
815                    0 => (0u64, 1u64, 0u64),
816                    len => {
817                        if len % 2 == 1 {
818                            log_warn!(
819                                "unexpectedly received {} events: there should always be an even number of non-empty events.",
820                                len
821                            );
822                        }
823                        (1u64, 0u64, len as u64)
824                    }
825                };
826
827                let device_id = match &event.device_info {
828                    Some(TouchDeviceInfo { id: Some(id), .. }) => Some(*id),
829                    _ => None,
830                };
831
832                let dev = match device_id {
833                    Some(id) => self.devices.get_mut(&id).unwrap_or(default_touch_device),
834                    None => default_touch_device,
835                };
836
837                #[allow(clippy::collection_is_never_read)]
838                let mut tracked_leases = vec![];
839                if let Some(lease) = event.wake_lease.take() {
840                    if let Some(status) = &dev.inspect_status {
841                        tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
842                    }
843                }
844
845                if let Some(dev_inspect_status) = &dev.inspect_status {
846                    dev_inspect_status.count_total_received_events(1);
847                    dev_inspect_status.count_total_ignored_events(ignored_events);
848                    dev_inspect_status.count_total_converted_events(converted_events);
849                    dev_inspect_status.count_total_generated_events(
850                        generated_events,
851                        batch.event_time.into_nanos().try_into().unwrap(),
852                    );
853                } else {
854                    log_warn!("unable to record inspect for touch device");
855                }
856
857                dev.open_files.lock().retain(|f| {
858                    let Some(file) = f.upgrade() else {
859                        log_warn!("Dropping input file for touch that failed to upgrade");
860                        return false;
861                    };
862                    match &file.inspect_status {
863                        Some(file_inspect_status) => {
864                            file_inspect_status.count_received_events(1);
865                            file_inspect_status.count_ignored_events(ignored_events);
866                            file_inspect_status.count_converted_events(converted_events);
867                        }
868                        None => {
869                            log_warn!("unable to record inspect within the input file")
870                        }
871                    }
872                    if !batch.events.is_empty() {
873                        if let Some(file_inspect_status) = &file.inspect_status {
874                            file_inspect_status.count_generated_events(
875                                generated_events,
876                                batch.event_time.into_nanos().try_into().unwrap(),
877                            );
878                        }
879                        file.add_events(batch.events.clone());
880                    }
881
882                    true
883                });
884
885                responder.send().expect("touch buttons responder failed to respond");
886
887                batch.touch_buttons
888            }
889            fuipolicy::TouchButtonsListenerRequest::_UnknownMethod { ordinal, .. } => {
890                log_warn!("Received an unknown method with ordinal {ordinal}");
891                touch_buttons_were_pressed.clone()
892            }
893        }
894    }
895
896    fn process_mouse_event(
897        self: &mut Self,
898        default_mouse_device: &mut DeviceState,
899        mouse_events: Vec<FidlMouseEvent>,
900    ) {
901        fuchsia_trace::duration!("input", "starnix_process_mouse_event");
902        for e in &mouse_events {
903            if let Some(trace_flow_id) = e.trace_flow_id {
904                fuchsia_trace::flow_end!("input", "dispatch_event_to_client", trace_flow_id.into());
905            }
906        }
907        // TODO(https://fxbug.dev/563345995): `num_received_events` counts the whole
908        // batch and `num_ignored_events` accumulates across devices, yet both are
909        // recorded against every device in the loop below. With more than one mouse
910        // device in use simultaneously these counters over-report and violate the
911        // inspect invariants. Scope them per device before multi-mouse is supported.
912        let num_received_events: u64 = mouse_events.len().try_into().unwrap();
913        let mut num_ignored_events: u64 = 0;
914
915        let (events_by_device, ignored_events) = group_mouse_events_by_device_id(mouse_events);
916        num_ignored_events += ignored_events;
917
918        for (device_id, mut events) in events_by_device {
919            fuchsia_trace::duration_begin!("input", "starnix_process_per_device_mouse_event");
920
921            let dev = self.devices.get_mut(&device_id).unwrap_or(default_mouse_device);
922
923            let mut num_converted_events: u64 = 0;
924            let mut num_unexpected_events: u64 = 0;
925            let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
926
927            #[allow(clippy::collection_is_never_read)]
928            let mut tracked_leases = vec![];
929            for event in &mut events {
930                if let Some(lease) = event.wake_lease.take() {
931                    if let Some(status) = &dev.inspect_status {
932                        tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
933                    }
934                }
935            }
936
937            let last_event_time_ns: i64;
938            if let InputDeviceType::Mouse(ref mut converter) = dev.device_type {
939                let mut batch = converter.handle(events);
940                new_events.append(&mut batch.events);
941                num_converted_events += batch.count_converted_events;
942                num_ignored_events += batch.count_ignored_events;
943                num_unexpected_events += batch.count_unexpected_events;
944                last_event_time_ns = batch.last_event_time_ns;
945            } else {
946                fuchsia_trace::duration_end!("input", "starnix_process_per_device_mouse_event");
947                log_warn!(
948                    "Non mouse device received mouse events: device_id = {}, device_type = {}",
949                    device_id,
950                    dev.device_type
951                );
952                continue;
953            }
954
955            if !new_events.is_empty() {
956                dev.registration.ensure_registered();
957            }
958
959            if let Some(dev_inspect_status) = &dev.inspect_status {
960                dev_inspect_status.count_total_received_events(num_received_events);
961                dev_inspect_status.count_total_ignored_events(num_ignored_events);
962                dev_inspect_status.count_total_unexpected_events(num_unexpected_events);
963                dev_inspect_status.count_total_converted_events(num_converted_events);
964                if !new_events.is_empty() {
965                    dev_inspect_status.count_total_generated_events(
966                        new_events.len().try_into().unwrap(),
967                        last_event_time_ns,
968                    );
969                }
970            } else {
971                log_warn!(
972                    "unable to record inspect for device_id: {}, device_type: {}",
973                    device_id,
974                    dev.device_type
975                );
976            }
977
978            fuchsia_trace::duration_end!("input", "starnix_process_per_device_mouse_event");
979            let mut open_files = dev.open_files.lock();
980            if !open_files.has_been_opened && !new_events.is_empty() {
981                open_files.buffered_events.extend(new_events.iter().copied());
982            }
983            open_files.retain(|f| {
984                let Some(file) = f.upgrade() else {
985                    log_warn!("Dropping input file for mouse that failed to upgrade");
986                    return false;
987                };
988                if let Some(file_inspect_status) = &file.inspect_status {
989                    file_inspect_status.count_received_events(num_received_events);
990                    file_inspect_status.count_ignored_events(num_ignored_events);
991                    file_inspect_status.count_unexpected_events(num_unexpected_events);
992                    file_inspect_status.count_converted_events(num_converted_events);
993                }
994                if !new_events.is_empty() {
995                    if let Some(file_inspect_status) = &file.inspect_status {
996                        file_inspect_status.count_generated_events(
997                            new_events.len().try_into().unwrap(),
998                            last_event_time_ns,
999                        );
1000                    }
1001                    file.add_events(new_events.clone().into_iter().collect());
1002                }
1003                true
1004            });
1005        }
1006    }
1007}
1008
1009fn setup_touch_relay(
1010    kernel: &Arc<Kernel>,
1011    event_proxy_mode: EventProxyMode,
1012    touch_source_client_end: ClientEnd<fuipointer::TouchSourceV2Marker>,
1013    default_touch_device_opened_files: OpenedFiles,
1014    device_inspect_status: Option<Arc<InputDeviceStatus>>,
1015) -> (
1016    DeviceState,
1017    fuipointer::TouchSourceV2Proxy,
1018    ContainerWakingStream<fuipointer::TouchSourceV2EventStream>,
1019) {
1020    let touch_counter_name = "touch";
1021    let default_touch_device = DeviceState {
1022        device_type: InputDeviceType::Touch(FuchsiaTouchEventToLinuxTouchEventConverter::create()),
1023        open_files: default_touch_device_opened_files,
1024        inspect_status: device_inspect_status,
1025        registration: DeviceRegistration::Registered,
1026    };
1027    let (touch_source_proxy, counter) = match event_proxy_mode {
1028        EventProxyMode::WakeContainer => {
1029            // Proxy the touch events through the Starnix runner. This allows touch events to
1030            // wake the container when it is suspended.
1031            let (touch_source_channel, counter) = create_proxy_for_wake_events_counter(
1032                touch_source_client_end.into_channel(),
1033                touch_counter_name.to_string(),
1034            );
1035            (
1036                fuipointer::TouchSourceV2Proxy::new(fidl::AsyncChannel::from_channel(
1037                    touch_source_channel,
1038                )),
1039                Some(counter),
1040            )
1041        }
1042        EventProxyMode::None => (touch_source_client_end.into_proxy(), None),
1043    };
1044    let waking_stream = ContainerWakingStream::new(
1045        kernel.suspend_resume_manager.add_message_counter(touch_counter_name, counter),
1046        touch_source_proxy.take_event_stream(),
1047    );
1048    (default_touch_device, touch_source_proxy, waking_stream)
1049}
1050
1051fn setup_keyboard_relay(
1052    keyboard: KeyboardSynchronousProxy,
1053    view_ref: fuiviews::ViewRef,
1054    default_keyboard_device_opened_files: OpenedFiles,
1055    device_inspect_status: Option<Arc<InputDeviceStatus>>,
1056) -> (DeviceState, KeyboardListenerRequestStream, KeyboardSynchronousProxy) {
1057    let default_keyboard_device = DeviceState {
1058        device_type: InputDeviceType::Keyboard,
1059        open_files: default_keyboard_device_opened_files,
1060        inspect_status: device_inspect_status,
1061        registration: DeviceRegistration::Registered,
1062    };
1063    let (keyboard_listener, event_stream) =
1064        fidl::endpoints::create_request_stream::<KeyboardListenerMarker>();
1065    if let Err(e) =
1066        keyboard.add_listener(view_ref, keyboard_listener, zx::MonotonicInstant::INFINITE)
1067    {
1068        log_warn!("Could not register keyboard listener: {:?}", e);
1069    }
1070
1071    (default_keyboard_device, event_stream, keyboard)
1072}
1073
1074fn setup_button_relay(
1075    kernel: &Arc<Kernel>,
1076    registry_proxy: fuipolicy::DeviceListenerRegistrySynchronousProxy,
1077    event_proxy_mode: EventProxyMode,
1078    default_keyboard_device_opened_files: OpenedFiles,
1079    device_inspect_status: Option<Arc<InputDeviceStatus>>,
1080) -> (
1081    DeviceState,
1082    ContainerWakingStream<fuipolicy::MediaButtonsListenerRequestStream>,
1083    ContainerWakingStream<fuipolicy::TouchButtonsListenerRequestStream>,
1084) {
1085    let default_keyboard_device = DeviceState {
1086        device_type: InputDeviceType::Keyboard,
1087        open_files: default_keyboard_device_opened_files,
1088        inspect_status: device_inspect_status,
1089        registration: DeviceRegistration::Registered,
1090    };
1091    let media_buttons_name = "media buttons";
1092    let touch_buttons_name = "touch buttons";
1093
1094    let (remote_media_button_client, remote_media_button_server) =
1095        fidl::endpoints::create_endpoints::<fuipolicy::MediaButtonsListenerMarker>();
1096    if let Err(e) =
1097        registry_proxy.register_listener(remote_media_button_client, zx::MonotonicInstant::INFINITE)
1098    {
1099        log_warn!("Failed to register media buttons listener: {:?}", e);
1100    }
1101
1102    let (remote_touch_button_client, remote_touch_button_server) =
1103        fidl::endpoints::create_endpoints::<fuipolicy::TouchButtonsListenerMarker>();
1104    if let Err(e) = registry_proxy
1105        .register_touch_buttons_listener(remote_touch_button_client, zx::MonotonicInstant::INFINITE)
1106    {
1107        log_warn!("Failed to register touch buttons listener: {:?}", e);
1108    }
1109
1110    let (
1111        local_media_buttons_listener_stream,
1112        media_buttons_counter,
1113        local_touch_buttons_listener_stream,
1114        touch_buttons_counter,
1115    ) = match event_proxy_mode {
1116        EventProxyMode::WakeContainer => {
1117            let (local_media_buttons_channel, media_buttons_counter) =
1118                create_proxy_for_wake_events_counter(
1119                    remote_media_button_server.into_channel(),
1120                    media_buttons_name.to_string(),
1121                );
1122            let local_media_buttons_listener_stream =
1123                fuipolicy::MediaButtonsListenerRequestStream::from_channel(
1124                    fidl::AsyncChannel::from_channel(local_media_buttons_channel),
1125                );
1126
1127            let (local_touch_buttons_channel, touch_buttons_counter) =
1128                create_proxy_for_wake_events_counter(
1129                    remote_touch_button_server.into_channel(),
1130                    touch_buttons_name.to_string(),
1131                );
1132            let local_touch_buttons_listener_stream =
1133                fuipolicy::TouchButtonsListenerRequestStream::from_channel(
1134                    fidl::AsyncChannel::from_channel(local_touch_buttons_channel),
1135                );
1136            (
1137                local_media_buttons_listener_stream,
1138                Some(media_buttons_counter),
1139                local_touch_buttons_listener_stream,
1140                Some(touch_buttons_counter),
1141            )
1142        }
1143        EventProxyMode::None => (
1144            remote_media_button_server.into_stream(),
1145            None,
1146            remote_touch_button_server.into_stream(),
1147            None,
1148        ),
1149    };
1150
1151    (
1152        default_keyboard_device,
1153        ContainerWakingStream::new(
1154            kernel
1155                .suspend_resume_manager
1156                .add_message_counter(media_buttons_name, media_buttons_counter),
1157            local_media_buttons_listener_stream,
1158        ),
1159        ContainerWakingStream::new(
1160            kernel
1161                .suspend_resume_manager
1162                .add_message_counter(touch_buttons_name, touch_buttons_counter),
1163            local_touch_buttons_listener_stream,
1164        ),
1165    )
1166}
1167
1168fn setup_mouse_relay(
1169    kernel: &Arc<Kernel>,
1170    event_proxy_mode: EventProxyMode,
1171    mouse_source_client_end: ClientEnd<fuipointer::MouseSourceV2Marker>,
1172    default_mouse_device: Option<crate::InputDevice>,
1173) -> (
1174    DeviceState,
1175    fuipointer::MouseSourceV2Proxy,
1176    ContainerWakingStream<fuipointer::MouseSourceV2EventStream>,
1177) {
1178    let mouse_counter_name = "mouse";
1179    let (open_files, inspect_status, registration) = match default_mouse_device {
1180        Some(dev) => (
1181            dev.open_files.clone(),
1182            Some(dev.inspect_status.clone()),
1183            DeviceRegistration::pending(kernel.clone(), dev, DEFAULT_MOUSE_DEVICE_ID),
1184        ),
1185        None => (Default::default(), None, DeviceRegistration::Registered),
1186    };
1187    let default_mouse_device = DeviceState {
1188        device_type: InputDeviceType::Mouse(FuchsiaMouseEventToLinuxMouseEventConverter::create()),
1189        open_files,
1190        inspect_status,
1191        registration,
1192    };
1193    let (mouse_source_proxy, counter) = match event_proxy_mode {
1194        EventProxyMode::WakeContainer => {
1195            // Proxy the mouse events through the Starnix runner. This allows mouse events to
1196            // wake the container when it is suspended.
1197            let (mouse_source_channel, resume_event) = create_proxy_for_wake_events_counter(
1198                mouse_source_client_end.into_channel(),
1199                "mouse".to_string(),
1200            );
1201            (
1202                fuipointer::MouseSourceV2Proxy::new(fidl::AsyncChannel::from_channel(
1203                    mouse_source_channel,
1204                )),
1205                Some(resume_event),
1206            )
1207        }
1208        EventProxyMode::None => (mouse_source_client_end.into_proxy(), None),
1209    };
1210
1211    let waking_stream = ContainerWakingStream::new(
1212        kernel.suspend_resume_manager.add_message_counter(mouse_counter_name, counter),
1213        mouse_source_proxy.take_event_stream(),
1214    );
1215
1216    (default_mouse_device, mouse_source_proxy, waking_stream)
1217}
1218
1219fn group_mouse_events_by_device_id(
1220    events: Vec<FidlMouseEvent>,
1221) -> (SortedVecMap<DeviceId, Vec<FidlMouseEvent>>, u64) {
1222    let mut events_by_device: SortedVecMap<u32, Vec<FidlMouseEvent>> = SortedVecMap::new();
1223    let mut ignored_events: u64 = 0;
1224    for e in events {
1225        match e {
1226            FidlMouseEvent { pointer_sample: Some(ref sample), .. } => {
1227                let id = sample.device_id.unwrap_or(DEFAULT_MOUSE_DEVICE_ID);
1228                if let Some(vec) = events_by_device.get_mut(&id) {
1229                    vec.push(e);
1230                } else {
1231                    events_by_device.insert(id, vec![e]);
1232                }
1233            }
1234            _ => {
1235                ignored_events += 1;
1236            }
1237        }
1238    }
1239
1240    (events_by_device, ignored_events)
1241}
1242
1243fn group_touch_events_by_device_id(
1244    events: Vec<FidlTouchEvent>,
1245) -> (SortedVecMap<DeviceId, Vec<FidlTouchEvent>>, u64) {
1246    let mut events_by_device: SortedVecMap<u32, Vec<FidlTouchEvent>> = SortedVecMap::new();
1247    let mut ignored_events: u64 = 0;
1248    for e in events {
1249        match e {
1250            FidlTouchEvent {
1251                pointer_sample: Some(TouchPointerSample { interaction: Some(id), .. }),
1252                ..
1253            } => {
1254                if let Some(vec) = events_by_device.get_mut(&id.device_id) {
1255                    vec.push(e);
1256                } else {
1257                    events_by_device.insert(id.device_id, vec![e]);
1258                }
1259            }
1260            _ => {
1261                ignored_events += 1;
1262            }
1263        }
1264    }
1265
1266    (events_by_device, ignored_events)
1267}
1268
1269#[cfg(test)]
1270pub async fn start_input_relays_for_test(
1271    current_task: &starnix_core::task::CurrentTask,
1272    event_proxy_mode: EventProxyMode,
1273) -> (
1274    Arc<InputEventsRelayHandle>,
1275    crate::InputDevice,
1276    crate::InputDevice,
1277    crate::InputDevice,
1278    starnix_core::vfs::FileHandle,
1279    starnix_core::vfs::FileHandle,
1280    starnix_core::vfs::FileHandle,
1281    fuipointer::TouchSourceV2RequestStream,
1282    fuipointer::MouseSourceV2RequestStream,
1283    fidl_fuchsia_ui_input3::KeyboardListenerProxy,
1284    fuipolicy::MediaButtonsListenerProxy,
1285    fuipolicy::TouchButtonsListenerProxy,
1286) {
1287    let inspector = fuchsia_inspect::Inspector::default();
1288
1289    let touch_device = crate::InputDevice::new_touch(700, 1200, inspector.root());
1290    let touch_file = touch_device.open_test(current_task).expect("Failed to create input file");
1291
1292    let keyboard_device = crate::InputDevice::new_keyboard(inspector.root());
1293    let keyboard_file =
1294        keyboard_device.open_test(current_task).expect("Failed to create input file");
1295
1296    let mouse_device = crate::InputDevice::new_mouse(inspector.root());
1297    let mouse_file = mouse_device.open_test(current_task).expect("Failed to create input file");
1298
1299    let (touch_source_client_end, touch_source_stream) =
1300        fidl::endpoints::create_request_stream::<fuipointer::TouchSourceV2Marker>();
1301    let (mouse_source_client_end, mouse_stream) =
1302        fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
1303    let (keyboard_proxy, mut keyboard_stream) =
1304        fidl::endpoints::create_sync_proxy_and_stream::<fidl_fuchsia_ui_input3::KeyboardMarker>();
1305    let view_ref_pair = fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
1306    let (device_registry_proxy, mut device_listener_stream) =
1307        fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>();
1308
1309    let (relay, relay_handle) = new_input_relay();
1310    relay.start_relays(
1311        &current_task.kernel(),
1312        event_proxy_mode,
1313        touch_source_client_end,
1314        keyboard_proxy,
1315        mouse_source_client_end,
1316        view_ref_pair.view_ref,
1317        device_registry_proxy,
1318        touch_device.open_files.clone(),
1319        keyboard_device.open_files.clone(),
1320        Some(mouse_device.clone()),
1321        Some(touch_device.inspect_status.clone()),
1322        Some(keyboard_device.inspect_status.clone()),
1323    );
1324
1325    let keyboard_listener = match keyboard_stream.next().await {
1326        Some(Ok(fidl_fuchsia_ui_input3::KeyboardRequest::AddListener {
1327            view_ref: _,
1328            listener,
1329            responder,
1330        })) => {
1331            let _ = responder.send();
1332            listener.into_proxy()
1333        }
1334        _ => {
1335            panic!("Failed to get event");
1336        }
1337    };
1338
1339    let media_buttons_listener = match device_listener_stream.next().await {
1340        Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
1341            listener,
1342            responder,
1343        })) => {
1344            let _ = responder.send();
1345            listener.into_proxy()
1346        }
1347        _ => {
1348            panic!("Failed to get event");
1349        }
1350    };
1351
1352    let touch_buttons_listener = match device_listener_stream.next().await {
1353        Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
1354            listener,
1355            responder,
1356        })) => {
1357            let _ = responder.send();
1358            listener.into_proxy()
1359        }
1360        _ => {
1361            panic!("Failed to get event");
1362        }
1363    };
1364
1365    (
1366        relay_handle,
1367        touch_device,
1368        keyboard_device,
1369        mouse_device,
1370        touch_file,
1371        keyboard_file,
1372        mouse_file,
1373        touch_source_stream,
1374        mouse_stream,
1375        keyboard_listener,
1376        media_buttons_listener,
1377        touch_buttons_listener,
1378    )
1379}
1380
1381#[cfg(test)]
1382mod test {
1383    use super::*;
1384    use anyhow::anyhow;
1385    use fidl_fuchsia_ui_input::{
1386        MediaButtonsEvent, TouchButton, TouchButtonsEvent, TouchDeviceInfo,
1387    };
1388    use fidl_fuchsia_ui_input3 as fuiinput;
1389    use fuipointer::{
1390        EventPhase, MouseEvent, MousePointerSample, TouchEvent, TouchInteractionId,
1391        TouchPointerSample, TouchSourceV2Request, TouchSourceV2RequestStream,
1392    };
1393    use starnix_core::task::CurrentTask;
1394    use starnix_core::testing::spawn_kernel_and_run;
1395    use starnix_core::vfs::{FileHandle, FileObject, VecOutputBuffer};
1396
1397    use starnix_types::time::timeval_from_time;
1398    use starnix_uapi::errors::{EAGAIN, Errno};
1399    use starnix_uapi::input_id;
1400    use starnix_uapi::open_flags::OpenFlags;
1401    use zerocopy::FromBytes as _;
1402
1403    const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
1404
1405    // Sends `touch_events` to the client stream and waits for `AcknowledgeEvents`.
1406    async fn answer_next_touch_watch_request(
1407        request_stream: &mut TouchSourceV2RequestStream,
1408        touch_events: Vec<TouchEvent>,
1409    ) {
1410        let control_handle = request_stream.control_handle();
1411        control_handle
1412            .send_on_touch_events(touch_events, 1)
1413            .expect("failure sending OnTouchEvents");
1414        match request_stream.next().await {
1415            Some(Ok(TouchSourceV2Request::AcknowledgeEvents { .. })) => {}
1416            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1417        }
1418    }
1419
1420    // Sends `mouse_events` to the client stream and waits for `AcknowledgeEvents`.
1421    async fn answer_next_mouse_watch_request(
1422        request_stream: &mut fuipointer::MouseSourceV2RequestStream,
1423        mouse_events: Vec<MouseEvent>,
1424    ) {
1425        let control_handle = request_stream.control_handle();
1426        control_handle
1427            .send_on_mouse_events(mouse_events, 1)
1428            .expect("failure sending OnMouseEvents");
1429        match request_stream.next().await {
1430            Some(Ok(fuipointer::MouseSourceV2Request::AcknowledgeEvents { .. })) => {}
1431            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1432        }
1433    }
1434
1435    fn make_empty_touch_event(device_id: u32) -> TouchEvent {
1436        TouchEvent {
1437            pointer_sample: Some(TouchPointerSample {
1438                interaction: Some(TouchInteractionId {
1439                    pointer_id: 0,
1440                    device_id,
1441                    interaction_id: 0,
1442                }),
1443                ..Default::default()
1444            }),
1445            ..Default::default()
1446        }
1447    }
1448
1449    fn make_touch_event_with_phase_device_id(
1450        phase: EventPhase,
1451        pointer_id: u32,
1452        device_id: u32,
1453    ) -> TouchEvent {
1454        make_touch_event_with_phase_device_id_position(phase, pointer_id, device_id, 0.0, 0.0)
1455    }
1456
1457    fn make_touch_event_with_phase_device_id_position(
1458        phase: EventPhase,
1459        pointer_id: u32,
1460        device_id: u32,
1461        x: f32,
1462        y: f32,
1463    ) -> TouchEvent {
1464        TouchEvent {
1465            timestamp: Some(0),
1466            pointer_sample: Some(TouchPointerSample {
1467                position_in_viewport: Some([x, y]),
1468                phase: Some(phase),
1469                interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
1470                ..Default::default()
1471            }),
1472            ..Default::default()
1473        }
1474    }
1475
1476    fn make_mouse_wheel_event(scroll_v_ticks: i64, device_id: u32) -> MouseEvent {
1477        MouseEvent {
1478            timestamp: Some(0),
1479            pointer_sample: Some(MousePointerSample {
1480                device_id: Some(device_id),
1481                scroll_v: Some(scroll_v_ticks),
1482                ..Default::default()
1483            }),
1484            ..Default::default()
1485        }
1486    }
1487
1488    fn read_uapi_events(file: &FileHandle, current_task: &CurrentTask) -> Vec<uapi::input_event> {
1489        std::iter::from_fn(|| {
1490            let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
1491            match file.read(current_task, &mut event_bytes) {
1492                Ok(INPUT_EVENT_SIZE) => Some(
1493                    uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
1494                        .map_err(|_| anyhow!("failed to read input_event from buffer")),
1495                ),
1496                Ok(other_size) => {
1497                    Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
1498                }
1499                Err(Errno { code: EAGAIN, .. }) => None,
1500                Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
1501            }
1502        })
1503        .enumerate()
1504        .map(|(i, read_res)| match read_res {
1505            Ok(event) => event,
1506            Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
1507        })
1508        .collect()
1509    }
1510
1511    fn create_test_touch_device(
1512        current_task: &CurrentTask,
1513        input_relay: Arc<InputEventsRelayHandle>,
1514        device_id: u32,
1515    ) -> FileHandle {
1516        let open_files: OpenedFiles = Default::default();
1517        input_relay.add_touch_device(device_id, open_files.clone(), None);
1518        let inspector = fuchsia_inspect::Inspector::default();
1519        let device_file = Arc::new(InputFile::new_touch(
1520            input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1521            1000,
1522            1000,
1523            inspector.root(),
1524        ));
1525        open_files.lock().push(Arc::downgrade(&device_file));
1526
1527        let root_namespace_node = current_task
1528            .lookup_path_from_root(".".into())
1529            .expect("failed to get namespace node for root");
1530
1531        FileObject::new(
1532            &current_task,
1533            Box::new(crate::input_file::ArcInputFile(device_file)),
1534            root_namespace_node,
1535            OpenFlags::empty(),
1536        )
1537        .expect("FileObject::new failed")
1538    }
1539
1540    fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1541        uapi::input_event {
1542            time: timeval_from_time(zx::MonotonicInstant::from_nanos(0)),
1543            type_: ty as u16,
1544            code: code as u16,
1545            value,
1546        }
1547    }
1548
1549    #[::fuchsia::test]
1550    async fn route_touch_event_by_device_id() {
1551        spawn_kernel_and_run(async move |current_task| {
1552            // Set up resources.
1553
1554            let (
1555                input_relay,
1556                _touch_device,
1557                _keyboard_device,
1558                _mouse_device,
1559                input_file,
1560                _keyboard_file,
1561                _mouse_file,
1562                mut touch_source_stream,
1563                _mouse_source_stream,
1564                _keyboard_listener,
1565                _media_buttons_listener,
1566                _touch_buttons_listener,
1567            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1568
1569            const DEVICE_ID: u32 = 10;
1570
1571            answer_next_touch_watch_request(
1572                &mut touch_source_stream,
1573                vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1574            )
1575            .await;
1576
1577            // Wait for another `Watch` to ensure input_file done processing the first reply.
1578            // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1579            // `uapi::input_event`s.
1580            answer_next_touch_watch_request(
1581                &mut touch_source_stream,
1582                vec![make_empty_touch_event(DEVICE_ID)],
1583            )
1584            .await;
1585
1586            // Consume all of the `uapi::input_event`s that are available.
1587            let events = read_uapi_events(&input_file, &current_task);
1588            // Default device receive events because no matched device.
1589            assert_ne!(events.len(), 0);
1590
1591            // add a device, mock uinput.
1592            let device_id_10_file =
1593                create_test_touch_device(&current_task, input_relay.clone(), DEVICE_ID);
1594
1595            answer_next_touch_watch_request(
1596                &mut touch_source_stream,
1597                vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1598            )
1599            .await;
1600
1601            answer_next_touch_watch_request(
1602                &mut touch_source_stream,
1603                vec![make_empty_touch_event(DEVICE_ID)],
1604            )
1605            .await;
1606
1607            let events = read_uapi_events(&input_file, &current_task);
1608            // Default device should not receive events because they matched device id 10.
1609            assert_eq!(events.len(), 0);
1610
1611            let events = read_uapi_events(&device_id_10_file, &current_task);
1612            // file of device id 10 should receive events.
1613            assert_ne!(events.len(), 0);
1614        })
1615        .await;
1616    }
1617
1618    #[::fuchsia::test]
1619    async fn route_touch_event_with_wake_lease() {
1620        spawn_kernel_and_run(async move |current_task| {
1621            let (
1622                _input_relay,
1623                touch_device,
1624                _keyboard_device,
1625                _mouse_device,
1626                _input_file,
1627                _keyboard_file,
1628                _mouse_file,
1629                mut touch_source_stream,
1630                _mouse_source_stream,
1631                _keyboard_listener,
1632                _media_buttons_listener,
1633                _touch_buttons_listener,
1634            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1635
1636            const DEVICE_ID: u32 = 10;
1637            let mut event = make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID);
1638            let (p1, _p2) = fidl::EventPair::create();
1639            event.wake_lease = Some(p1);
1640
1641            answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1642
1643            // Wait for another `Watch` to ensure input_file done processing the first reply.
1644            answer_next_touch_watch_request(
1645                &mut touch_source_stream,
1646                vec![make_empty_touch_event(DEVICE_ID)],
1647            )
1648            .await;
1649
1650            let status = &touch_device.inspect_status;
1651            assert_eq!(
1652                status
1653                    .total_events_with_wake_lease_count
1654                    .load(std::sync::atomic::Ordering::Relaxed),
1655                1
1656            );
1657            assert_eq!(
1658                status.active_wake_leases_count.load(std::sync::atomic::Ordering::Relaxed),
1659                0
1660            );
1661        })
1662        .await;
1663    }
1664
1665    #[::fuchsia::test]
1666    async fn route_touch_event_by_device_id_multi_device_events_in_one_sequence() {
1667        spawn_kernel_and_run(async move |current_task| {
1668            let (
1669                input_relay,
1670                _touch_device,
1671                _keyboard_device,
1672                _mouse_device,
1673                _input_file,
1674                _keyboard_file,
1675                _mouse_file,
1676                mut touch_source_stream,
1677                _mouse_source_stream,
1678                _keyboard_listener,
1679                _media_buttons_listener,
1680                _touch_buttons_listener,
1681            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1682
1683            const DEVICE_ID_10: u32 = 10;
1684            const DEVICE_ID_11: u32 = 11;
1685
1686            let device_id_10_file =
1687                create_test_touch_device(&current_task, input_relay.clone(), DEVICE_ID_10);
1688
1689            let device_id_11_file =
1690                create_test_touch_device(&current_task, input_relay.clone(), DEVICE_ID_11);
1691
1692            // 2 pointer down on different touch device.
1693            answer_next_touch_watch_request(
1694                &mut touch_source_stream,
1695                vec![
1696                    make_touch_event_with_phase_device_id_position(
1697                        EventPhase::Add,
1698                        1,
1699                        DEVICE_ID_10,
1700                        10.0,
1701                        20.0,
1702                    ),
1703                    make_touch_event_with_phase_device_id_position(
1704                        EventPhase::Add,
1705                        2,
1706                        DEVICE_ID_11,
1707                        30.0,
1708                        40.0,
1709                    ),
1710                ],
1711            )
1712            .await;
1713
1714            answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
1715
1716            let events_10 = read_uapi_events(&device_id_10_file, &current_task);
1717            let events_11 = read_uapi_events(&device_id_11_file, &current_task);
1718            assert_eq!(events_10.len(), events_11.len());
1719
1720            assert_eq!(
1721                events_10,
1722                vec![
1723                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1724                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1725                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1726                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1727                    make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1728                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1729                ]
1730            );
1731
1732            assert_eq!(
1733                events_11,
1734                vec![
1735                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1736                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1737                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 30),
1738                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 40),
1739                    make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1740                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1741                ]
1742            );
1743        })
1744        .await;
1745    }
1746
1747    #[::fuchsia::test]
1748    async fn route_key_event_by_device_id() {
1749        spawn_kernel_and_run(async move |current_task| {
1750            // Set up resources.
1751
1752            let (
1753                input_relay,
1754                _touch_device,
1755                _keyboard_device,
1756                _mouse_device,
1757                _touch_file,
1758                keyboard_file,
1759                _mouse_file,
1760                _touch_source_stream,
1761                _mouse_source_stream,
1762                keyboard_listener,
1763                _media_buttons_listener,
1764                _touch_buttons_listener,
1765            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1766
1767            const DEVICE_ID: u32 = 10;
1768
1769            let key_event = fuiinput::KeyEvent {
1770                timestamp: Some(0),
1771                type_: Some(fuiinput::KeyEventType::Pressed),
1772                key: Some(fidl_fuchsia_input::Key::A),
1773                device_id: Some(DEVICE_ID),
1774                ..Default::default()
1775            };
1776
1777            let _ = keyboard_listener.on_key_event(&key_event).await;
1778
1779            let events = read_uapi_events(&keyboard_file, &current_task);
1780            // Default device should receive events because no device device id is 10.
1781            assert_ne!(events.len(), 0);
1782
1783            // add a device, mock uinput.
1784            let open_files: OpenedFiles = Default::default();
1785            input_relay.add_keyboard_device(DEVICE_ID, open_files.clone(), None);
1786            let inspector = fuchsia_inspect::Inspector::default();
1787            let device_id_10_file = Arc::new(InputFile::new_keyboard(
1788                input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1789                inspector.root(),
1790            ));
1791            open_files.lock().push(Arc::downgrade(&device_id_10_file));
1792            let root_namespace_node = current_task
1793                .lookup_path_from_root(".".into())
1794                .expect("failed to get namespace node for root");
1795            let device_id_10_file_object = FileObject::new(
1796                &current_task,
1797                Box::new(crate::input_file::ArcInputFile(device_id_10_file)),
1798                root_namespace_node,
1799                OpenFlags::empty(),
1800            )
1801            .expect("FileObject::new failed");
1802
1803            let _ = keyboard_listener.on_key_event(&key_event).await;
1804
1805            let events = read_uapi_events(&keyboard_file, &current_task);
1806            // Default device should not receive events because they matched device id 10.
1807            assert_eq!(events.len(), 0);
1808
1809            let events = read_uapi_events(&device_id_10_file_object, &current_task);
1810            // file of device id 10 should receive events.
1811            assert_ne!(events.len(), 0);
1812
1813            std::mem::drop(keyboard_listener); // Close Zircon channel.
1814        })
1815        .await;
1816    }
1817
1818    #[::fuchsia::test]
1819    async fn route_media_button_event_by_device_id() {
1820        spawn_kernel_and_run(async move |current_task| {
1821            // Set up resources.
1822
1823            let (
1824                input_relay,
1825                _touch_device,
1826                _keyboard_device,
1827                _mouse_device,
1828                _touch_file,
1829                keyboard_file,
1830                _mouse_file,
1831                _touch_source_stream,
1832                _mouse_source_stream,
1833                _keyboard_listener,
1834                media_buttons_listener,
1835                _touch_buttons_listener,
1836            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1837
1838            const DEVICE_ID: u32 = 10;
1839
1840            let power_pressed_event = MediaButtonsEvent {
1841                volume: Some(0),
1842                mic_mute: Some(false),
1843                pause: Some(false),
1844                camera_disable: Some(false),
1845                power: Some(true),
1846                function: Some(false),
1847                device_id: Some(DEVICE_ID),
1848                ..Default::default()
1849            };
1850
1851            let _ = media_buttons_listener.on_event(power_pressed_event).await;
1852
1853            let events = read_uapi_events(&keyboard_file, &current_task);
1854            // Default device should receive events because no device device id is 10.
1855            assert_ne!(events.len(), 0);
1856
1857            // add a device, mock uinput.
1858            let open_files: OpenedFiles = Default::default();
1859            input_relay.add_keyboard_device(DEVICE_ID, open_files.clone(), None);
1860            let inspector = fuchsia_inspect::Inspector::default();
1861            let device_id_10_file = Arc::new(InputFile::new_keyboard(
1862                input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1863                inspector.root(),
1864            ));
1865            open_files.lock().push(Arc::downgrade(&device_id_10_file));
1866            let root_namespace_node = current_task
1867                .lookup_path_from_root(".".into())
1868                .expect("failed to get namespace node for root");
1869            let device_id_10_file_object = FileObject::new(
1870                &current_task,
1871                Box::new(crate::input_file::ArcInputFile(device_id_10_file)),
1872                root_namespace_node,
1873                OpenFlags::empty(),
1874            )
1875            .expect("FileObject::new failed");
1876
1877            let power_released_event = MediaButtonsEvent {
1878                volume: Some(0),
1879                mic_mute: Some(false),
1880                pause: Some(false),
1881                camera_disable: Some(false),
1882                power: Some(false),
1883                function: Some(false),
1884                device_id: Some(DEVICE_ID),
1885                ..Default::default()
1886            };
1887
1888            let _ = media_buttons_listener.on_event(power_released_event).await;
1889
1890            let events = read_uapi_events(&keyboard_file, &current_task);
1891            // Default device should not receive events because they matched device id 10.
1892            assert_eq!(events.len(), 0);
1893
1894            let events = read_uapi_events(&device_id_10_file_object, &current_task);
1895            // file of device id 10 should receive events.
1896            assert_ne!(events.len(), 0);
1897
1898            std::mem::drop(media_buttons_listener); // Close Zircon channel.
1899        })
1900        .await;
1901    }
1902
1903    #[::fuchsia::test]
1904    async fn route_touch_button_event_by_device_id() {
1905        spawn_kernel_and_run(async move |current_task| {
1906            // Set up resources.
1907
1908            let (
1909                input_relay,
1910                _touch_device,
1911                _keyboard_device,
1912                _mouse_device,
1913                touch_file,
1914                _keyboard_file,
1915                _mouse_file,
1916                _touch_source_stream,
1917                _mouse_source_stream,
1918                _keyboard_listener,
1919                _media_buttons_listener,
1920                touch_buttons_listener,
1921            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1922
1923            const DEVICE_ID: u32 = 10;
1924
1925            let palm_pressed_event: TouchButtonsEvent = TouchButtonsEvent {
1926                pressed_buttons: Some(vec![TouchButton::Palm]),
1927                device_info: Some(TouchDeviceInfo { id: Some(DEVICE_ID), ..Default::default() }),
1928                ..Default::default()
1929            };
1930
1931            let _ = touch_buttons_listener.on_event(palm_pressed_event).await;
1932
1933            let events = read_uapi_events(&touch_file, &current_task);
1934            // Default device should receive events because no device device id is 10.
1935            assert_ne!(events.len(), 0);
1936
1937            // add a device, mock uinput.
1938            let open_files: OpenedFiles = Default::default();
1939            input_relay.add_touch_device(DEVICE_ID, open_files.clone(), None);
1940            let device_id_10_file =
1941                create_test_touch_device(&current_task, input_relay.clone(), DEVICE_ID);
1942
1943            let palm_released_event: TouchButtonsEvent = TouchButtonsEvent {
1944                pressed_buttons: Some(vec![]),
1945                device_info: Some(TouchDeviceInfo { id: Some(DEVICE_ID), ..Default::default() }),
1946                ..Default::default()
1947            };
1948
1949            let _ = touch_buttons_listener.on_event(palm_released_event).await;
1950
1951            let events = read_uapi_events(&touch_file, &current_task);
1952            // Default device should not receive events because they matched device id 10.
1953            assert_eq!(events.len(), 0);
1954
1955            let events = read_uapi_events(&device_id_10_file, &current_task);
1956            // file of device id 10 should receive events.
1957            assert_ne!(events.len(), 0);
1958
1959            std::mem::drop(touch_buttons_listener); // Close Zircon channel.
1960        })
1961        .await;
1962    }
1963
1964    #[::fuchsia::test]
1965    async fn touch_device_multi_reader() {
1966        spawn_kernel_and_run(async move |current_task| {
1967            // Set up resources.
1968
1969            let (
1970                _input_relay,
1971                touch_device,
1972                _keyboard_device,
1973                _mouse_device,
1974                touch_reader1,
1975                _keyboard_file,
1976                _mouse_file,
1977                mut touch_source_stream,
1978                _mouse_source_stream,
1979                _keyboard_listener,
1980                _media_buttons_listener,
1981                _touch_buttons_listener,
1982            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
1983
1984            let touch_reader2 =
1985                touch_device.open_test(&current_task).expect("Failed to create input file");
1986
1987            const DEVICE_ID: u32 = 10;
1988
1989            answer_next_touch_watch_request(
1990                &mut touch_source_stream,
1991                vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1992            )
1993            .await;
1994
1995            // Wait for another `Watch` to ensure input_file done processing the first reply.
1996            // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1997            // `uapi::input_event`s.
1998            answer_next_touch_watch_request(
1999                &mut touch_source_stream,
2000                vec![make_empty_touch_event(DEVICE_ID)],
2001            )
2002            .await;
2003
2004            // Consume all of the `uapi::input_event`s that are available.
2005            let events_from_reader1 = read_uapi_events(&touch_reader1, &current_task);
2006            let events_from_reader2 = read_uapi_events(&touch_reader2, &current_task);
2007            assert_ne!(events_from_reader1.len(), 0);
2008            assert_eq!(events_from_reader1.len(), events_from_reader2.len());
2009        })
2010        .await;
2011    }
2012
2013    #[::fuchsia::test]
2014    async fn keyboard_device_multi_reader() {
2015        spawn_kernel_and_run(async move |current_task| {
2016            // Set up resources.
2017
2018            let (
2019                _input_relay,
2020                _touch_device,
2021                keyboard_device,
2022                _mouse_device,
2023                _touch_file,
2024                keyboard_reader1,
2025                _mouse_file,
2026                _touch_source_stream,
2027                _mouse_source_stream,
2028                keyboard_listener,
2029                _media_buttons_listener,
2030                _touch_buttons_listener,
2031            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
2032
2033            let keyboard_reader2 =
2034                keyboard_device.open_test(&current_task).expect("Failed to create input file");
2035
2036            const DEVICE_ID: u32 = 10;
2037
2038            let key_event = fuiinput::KeyEvent {
2039                timestamp: Some(0),
2040                type_: Some(fuiinput::KeyEventType::Pressed),
2041                key: Some(fidl_fuchsia_input::Key::A),
2042                device_id: Some(DEVICE_ID),
2043                ..Default::default()
2044            };
2045
2046            let _ = keyboard_listener.on_key_event(&key_event).await;
2047
2048            // Consume all of the `uapi::input_event`s that are available.
2049            let events_from_reader1 = read_uapi_events(&keyboard_reader1, &current_task);
2050            let events_from_reader2 = read_uapi_events(&keyboard_reader2, &current_task);
2051            assert_ne!(events_from_reader1.len(), 0);
2052            assert_eq!(events_from_reader1.len(), events_from_reader2.len());
2053        })
2054        .await;
2055    }
2056
2057    #[::fuchsia::test]
2058    async fn button_device_multi_reader() {
2059        spawn_kernel_and_run(async move |current_task| {
2060            // Set up resources.
2061
2062            let (
2063                _input_relay,
2064                _touch_device,
2065                keyboard_device,
2066                _mouse_device,
2067                _touch_file,
2068                keyboard_reader1,
2069                _mouse_file,
2070                _touch_source_stream,
2071                _mouse_source_stream,
2072                _keyboard_listener,
2073                media_buttons_listener,
2074                _touch_buttons_listener,
2075            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
2076
2077            let keyboard_reader2 =
2078                keyboard_device.open_test(&current_task).expect("Failed to create input file");
2079
2080            const DEVICE_ID: u32 = 10;
2081
2082            let power_pressed_event = MediaButtonsEvent {
2083                volume: Some(0),
2084                mic_mute: Some(false),
2085                pause: Some(false),
2086                camera_disable: Some(false),
2087                power: Some(true),
2088                function: Some(false),
2089                device_id: Some(DEVICE_ID),
2090                ..Default::default()
2091            };
2092
2093            let _ = media_buttons_listener.on_event(power_pressed_event).await;
2094
2095            // Consume all of the `uapi::input_event`s that are available.
2096            let events_from_reader1 = read_uapi_events(&keyboard_reader1, &current_task);
2097            let events_from_reader2 = read_uapi_events(&keyboard_reader2, &current_task);
2098            assert_ne!(events_from_reader1.len(), 0);
2099            assert_eq!(events_from_reader1.len(), events_from_reader2.len());
2100        })
2101        .await;
2102    }
2103
2104    #[::fuchsia::test]
2105    async fn mouse_device_multi_reader() {
2106        spawn_kernel_and_run(async move |current_task| {
2107            // Set up resources.
2108
2109            let (
2110                _input_relay,
2111                _touch_device,
2112                _keyboard_device,
2113                mouse_device,
2114                _touch_file,
2115                _keyboard_file,
2116                mouse_reader1,
2117                _touch_stream,
2118                mut mouse_stream,
2119                _keyboard_listener,
2120                _media_buttons_listener,
2121                _touch_buttons_listener,
2122            ) = start_input_relays_for_test(&current_task, EventProxyMode::None).await;
2123
2124            let mouse_reader2 =
2125                mouse_device.open_test(&current_task).expect("Failed to create input file");
2126
2127            const DEVICE_ID: u32 = 10;
2128
2129            answer_next_mouse_watch_request(
2130                &mut mouse_stream,
2131                vec![make_mouse_wheel_event(1, DEVICE_ID)],
2132            )
2133            .await;
2134
2135            // Wait for another `Watch` to ensure input_file done processing the first reply.
2136            // Use an empty `MouseEvent`, to minimize the chance that this event creates unexpected
2137            // `uapi::input_event`s.
2138            answer_next_mouse_watch_request(
2139                &mut mouse_stream,
2140                vec![make_mouse_wheel_event(0, DEVICE_ID)],
2141            )
2142            .await;
2143
2144            // Consume all of the `uapi::input_event`s that are available.
2145            let events_from_reader1 = read_uapi_events(&mouse_reader1, &current_task);
2146            let events_from_reader2 = read_uapi_events(&mouse_reader2, &current_task);
2147            assert_ne!(events_from_reader1.len(), 0);
2148            assert_eq!(events_from_reader1.len(), events_from_reader2.len());
2149        })
2150        .await;
2151    }
2152
2153    #[::fuchsia::test]
2154    async fn input_message_counters() {
2155        spawn_kernel_and_run(async move |current_task| {
2156            // Set up resources.
2157            let kernel = current_task.kernel().clone();
2158            let (
2159                _input_relay,
2160                _touch_device,
2161                _keyboard_device,
2162                _mouse_device,
2163                _touch_file,
2164                keyboard_file,
2165                _mouse_file,
2166                _touch_source_stream,
2167                _mouse_source_stream,
2168                keyboard_listener,
2169                _media_buttons_listener,
2170                _touch_buttons_listener,
2171            ) = start_input_relays_for_test(&current_task, EventProxyMode::WakeContainer).await;
2172
2173            const DEVICE_ID: u32 = 10;
2174
2175            let key_event = fuiinput::KeyEvent {
2176                timestamp: Some(0),
2177                type_: Some(fuiinput::KeyEventType::Pressed),
2178                key: Some(fidl_fuchsia_input::Key::A),
2179                device_id: Some(DEVICE_ID),
2180                ..Default::default()
2181            };
2182
2183            let _ = keyboard_listener.on_key_event(&key_event).await;
2184
2185            let events = read_uapi_events(&keyboard_file, &current_task);
2186            assert_ne!(events.len(), 0);
2187
2188            assert!(!kernel.suspend_resume_manager.has_nonzero_message_counter());
2189        })
2190        .await;
2191    }
2192
2193    #[::fuchsia::test]
2194    async fn mouse_device_lazily_registered_on_first_mouse_event() {
2195        spawn_kernel_and_run(async move |current_task| {
2196            let kernel = current_task.kernel().clone();
2197            let inspector = fuchsia_inspect::Inspector::default();
2198
2199            let touch_device = crate::InputDevice::new_touch(700, 1200, inspector.root());
2200            let keyboard_device = crate::InputDevice::new_keyboard(inspector.root());
2201            // Do not open `mouse_device` before registration so we test production ordering:
2202            // userspace can only open `/dev/input/event2` after `DeviceRegistry` registration.
2203            let mouse_device = crate::InputDevice::new_mouse(inspector.root());
2204
2205            let (touch_source_client_end, _touch_source_stream) =
2206                fidl::endpoints::create_request_stream::<fuipointer::TouchSourceV2Marker>();
2207            let (mouse_source_client_end, mut mouse_stream) =
2208                fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
2209            let (keyboard_proxy, mut keyboard_stream) =
2210                fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
2211            let view_ref_pair =
2212                fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2213            let (device_registry_proxy, mut device_listener_stream) =
2214                fidl::endpoints::create_sync_proxy_and_stream::<
2215                    fuipolicy::DeviceListenerRegistryMarker,
2216                >();
2217
2218            let (relay, _relay_handle) = new_input_relay();
2219            relay.start_relays(
2220                &kernel,
2221                EventProxyMode::None,
2222                touch_source_client_end,
2223                keyboard_proxy,
2224                mouse_source_client_end,
2225                view_ref_pair.view_ref,
2226                device_registry_proxy,
2227                touch_device.open_files.clone(),
2228                keyboard_device.open_files.clone(),
2229                Some(mouse_device.clone()),
2230                Some(touch_device.inspect_status.clone()),
2231                Some(keyboard_device.inspect_status.clone()),
2232            );
2233
2234            if let Some(Ok(fuiinput::KeyboardRequest::AddListener { responder, .. })) =
2235                keyboard_stream.next().await
2236            {
2237                let _ = responder.send();
2238            }
2239            if let Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
2240                responder,
2241                ..
2242            })) = device_listener_stream.next().await
2243            {
2244                let _ = responder.send();
2245            }
2246            if let Some(Ok(
2247                fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
2248                    responder,
2249                    ..
2250                },
2251            )) = device_listener_stream.next().await
2252            {
2253                let _ = responder.send();
2254            }
2255
2256            let mouse_dev_id = starnix_uapi::device_id::DeviceId::new(
2257                starnix_uapi::device_id::INPUT_MAJOR,
2258                DEFAULT_MOUSE_DEVICE_ID,
2259            );
2260            let next_dev_id = starnix_uapi::device_id::DeviceId::new(
2261                starnix_uapi::device_id::INPUT_MAJOR,
2262                DEFAULT_MOUSE_DEVICE_ID + 1,
2263            );
2264
2265            // Before any mouse event occurs, the mouse device should not be registered in
2266            // DeviceRegistry.
2267            assert!(
2268                kernel
2269                    .device_registry
2270                    .list_minor_devices(
2271                        starnix_core::device::DeviceMode::Char,
2272                        mouse_dev_id..next_dev_id,
2273                    )
2274                    .is_empty()
2275            );
2276
2277            // Send an empty/no-op mouse event (wheel delta 0) that produces no uapi input_events.
2278            answer_next_mouse_watch_request(
2279                &mut mouse_stream,
2280                vec![make_mouse_wheel_event(0, DEFAULT_MOUSE_DEVICE_ID)],
2281            )
2282            .await;
2283
2284            // Still should not be registered.
2285            assert!(
2286                kernel
2287                    .device_registry
2288                    .list_minor_devices(
2289                        starnix_core::device::DeviceMode::Char,
2290                        mouse_dev_id..next_dev_id,
2291                    )
2292                    .is_empty()
2293            );
2294
2295            // Send a real mouse event (wheel delta 1) to trigger Pending -> Registered, followed by
2296            // a second real mouse event (wheel delta 1) to synchronize the stream and exercise the
2297            // DeviceRegistration::Registered idempotency path.
2298            answer_next_mouse_watch_request(
2299                &mut mouse_stream,
2300                vec![make_mouse_wheel_event(1, DEFAULT_MOUSE_DEVICE_ID)],
2301            )
2302            .await;
2303            answer_next_mouse_watch_request(
2304                &mut mouse_stream,
2305                vec![make_mouse_wheel_event(1, DEFAULT_MOUSE_DEVICE_ID)],
2306            )
2307            .await;
2308            answer_next_mouse_watch_request(
2309                &mut mouse_stream,
2310                vec![make_mouse_wheel_event(0, DEFAULT_MOUSE_DEVICE_ID)],
2311            )
2312            .await;
2313
2314            // The mouse device should now be registered in DeviceRegistry.
2315            let registered = kernel.device_registry.list_minor_devices(
2316                starnix_core::device::DeviceMode::Char,
2317                mouse_dev_id..next_dev_id,
2318            );
2319            assert_eq!(registered.len(), 1);
2320            assert_eq!(registered[0].0, mouse_dev_id);
2321
2322            // Open the mouse device *after* registration (matching production ordering) and verify
2323            // that the converted events from the pre-open batches were buffered and flushed on
2324            // first open (2 wheel events * 2 uapi events [EV_REL, EV_SYN] each = 4 events).
2325            let mouse_file =
2326                mouse_device.open_test(&current_task).expect("Failed to open mouse file");
2327            let events = read_uapi_events(&mouse_file, &current_task);
2328            assert_eq!(events.len(), 4);
2329        })
2330        .await;
2331    }
2332}