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, LinuxEventWithTraceId, 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, MousePointerSample, TouchEvent as FidlTouchEvent,
14    TouchPointerSample, TouchResponse as FidlTouchResponse, TouchResponseType,
15    {self as fuipointer},
16};
17use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
18use futures::channel::oneshot::{self, Sender};
19use futures::executor::block_on;
20use futures::{FutureExt, StreamExt as _};
21use starnix_core::power::{
22    ContainerWakingProxy, ContainerWakingStream, create_proxy_for_wake_events_counter,
23};
24use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
25use starnix_core::task::{Kernel, LockedAndTask};
26use starnix_logging::{
27    log_warn, trace_duration, trace_duration_begin, trace_duration_end, trace_flow_end,
28};
29use starnix_modules_input_event_conversion::button_fuchsia_to_linux::{
30    parse_fidl_media_button_event, parse_fidl_touch_button_event,
31};
32use starnix_modules_input_event_conversion::key_fuchsia_to_linux::parse_fidl_keyboard_event_to_linux_input_event;
33use starnix_modules_input_event_conversion::touch_fuchsia_to_linux::FuchsiaTouchEventToLinuxTouchEventConverter;
34use starnix_sync::Mutex;
35use starnix_types::time::timeval_from_time;
36use starnix_uapi::uapi;
37use starnix_uapi::vfs::FdEvents;
38use std::cell::RefCell;
39use std::collections::{HashMap, VecDeque};
40use std::rc::Rc;
41use std::sync::{Arc, Weak};
42use {fidl_fuchsia_ui_policy as fuipolicy, fidl_fuchsia_ui_views as fuiviews};
43
44const INPUT_RELAY_ROLE_NAME: &str = "fuchsia.starnix.kthread.input_relay";
45
46#[derive(Clone, Copy)]
47pub enum EventProxyMode {
48    /// Don't proxy input events at all.
49    None,
50
51    /// Have the Starnix runner proxy events such that the container
52    /// will wake up if events are received while the container is
53    /// suspended.
54    WakeContainer,
55}
56
57pub type OpenedFiles = Arc<Mutex<Vec<Weak<InputFile>>>>;
58
59pub enum InputDeviceType {
60    Touch(FuchsiaTouchEventToLinuxTouchEventConverter),
61    Keyboard,
62    Mouse,
63}
64
65impl std::fmt::Display for InputDeviceType {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        match self {
68            InputDeviceType::Touch(_) => write!(f, "touch"),
69            InputDeviceType::Keyboard => write!(f, "keyboard"),
70            InputDeviceType::Mouse => write!(f, "mouse"),
71        }
72    }
73}
74
75pub struct DeviceState {
76    device_type: InputDeviceType,
77    open_files: OpenedFiles,
78    inspect_status: Option<Arc<InputDeviceStatus>>,
79}
80
81pub type DeviceId = u32;
82
83pub const DEFAULT_TOUCH_DEVICE_ID: DeviceId = 0;
84pub const DEFAULT_KEYBOARD_DEVICE_ID: DeviceId = 1;
85pub const DEFAULT_MOUSE_DEVICE_ID: DeviceId = 2;
86
87enum DeviceStateChange {
88    Add(DeviceId, DeviceState, Sender<()>),
89    Remove(DeviceId, Sender<()>),
90}
91
92pub fn new_input_relay() -> (InputEventsRelay, Arc<InputEventsRelayHandle>) {
93    let (sender, receiver) = unbounded();
94
95    (
96        InputEventsRelay { devices: HashMap::new(), receiver },
97        Arc::new(InputEventsRelayHandle { sender }),
98    )
99}
100
101pub struct InputEventsRelayHandle {
102    sender: UnboundedSender<DeviceStateChange>,
103}
104
105impl InputEventsRelayHandle {
106    pub fn add_touch_device(
107        self: &Arc<Self>,
108        device_id: DeviceId,
109        open_files: OpenedFiles,
110        inspect_status: Option<Arc<InputDeviceStatus>>,
111    ) {
112        let (sender, receiver) = oneshot::channel();
113        let _ = self.sender.unbounded_send(DeviceStateChange::Add(
114            device_id,
115            DeviceState {
116                device_type: InputDeviceType::Touch(
117                    FuchsiaTouchEventToLinuxTouchEventConverter::create(),
118                ),
119                open_files,
120                inspect_status,
121            },
122            sender,
123        ));
124        let _ = block_on(receiver);
125    }
126
127    pub fn add_keyboard_device(
128        &self,
129        device_id: DeviceId,
130        open_files: OpenedFiles,
131        inspect_status: Option<Arc<InputDeviceStatus>>,
132    ) {
133        let (sender, receiver) = oneshot::channel();
134        let _ = self.sender.unbounded_send(DeviceStateChange::Add(
135            device_id,
136            DeviceState { device_type: InputDeviceType::Keyboard, open_files, inspect_status },
137            sender,
138        ));
139        let _ = block_on(receiver);
140    }
141
142    pub fn remove_device(&self, device_id: DeviceId) {
143        let (sender, receiver) = oneshot::channel();
144        let _ = self.sender.unbounded_send(DeviceStateChange::Remove(device_id, sender));
145        let _ = block_on(receiver);
146    }
147}
148
149pub struct InputEventsRelay {
150    devices: HashMap<DeviceId, DeviceState>,
151    receiver: UnboundedReceiver<DeviceStateChange>,
152}
153
154impl InputEventsRelay {
155    // TODO(https://fxbug.dev/371602479): Use `fuchsia.ui.SupportedInputDevices` to create
156    // relays.
157    // start_relays will take over the ownership of InputEventsRelay.
158    pub fn start_relays(
159        mut self: Self,
160        kernel: &Kernel,
161        event_proxy_mode: EventProxyMode,
162        touch_source_client_end: ClientEnd<fuipointer::TouchSourceMarker>,
163        keyboard: KeyboardSynchronousProxy,
164        mouse_source_client_end: ClientEnd<fuipointer::MouseSourceMarker>,
165        view_ref: fuiviews::ViewRef,
166        registry_proxy: fuipolicy::DeviceListenerRegistrySynchronousProxy,
167        default_touch_device_opened_files: OpenedFiles,
168        default_keyboard_device_opened_files: OpenedFiles,
169        default_mouse_device_opened_files: OpenedFiles,
170        default_touch_device_inspect: Option<Arc<InputDeviceStatus>>,
171        default_keyboard_device_inspect: Option<Arc<InputDeviceStatus>>,
172        default_mouse_device_inspect: Option<Arc<InputDeviceStatus>>,
173    ) {
174        let f = async move |locked_and_task: LockedAndTask<'_>| {
175            let kernel = locked_and_task.current_task().kernel();
176            // touch
177            let previous_touch_event_disposition: Rc<RefCell<Vec<FidlTouchResponse>>> =
178                Default::default();
179            let touch_waking_fn = |p: &fuipointer::TouchSourceProxy| {
180                p.watch(&previous_touch_event_disposition.borrow_mut())
181            };
182            let (mut default_touch_device, touch_waking_proxy) = setup_touch_relay(
183                kernel,
184                event_proxy_mode,
185                touch_source_client_end,
186                default_touch_device_opened_files,
187                default_touch_device_inspect,
188            );
189            let mut touch_future = touch_waking_proxy.call(touch_waking_fn.clone()).fuse();
190
191            // mouse
192            let (mut default_mouse_device, mouse_waking_proxy) = setup_mouse_relay(
193                kernel,
194                event_proxy_mode,
195                mouse_source_client_end,
196                default_mouse_device_opened_files,
197                default_mouse_device_inspect,
198            );
199            let mut mouse_future =
200                mouse_waking_proxy.call(fuipointer::MouseSourceProxy::watch).fuse();
201
202            // keyboard
203            let (mut default_keyboard_device, mut keyboard_event_stream) = setup_keyboard_relay(
204                keyboard,
205                view_ref,
206                default_keyboard_device_opened_files.clone(),
207                default_keyboard_device_inspect.clone(),
208            );
209
210            // button
211            let (
212                mut default_button_device,
213                mut media_buttons_waking_stream,
214                mut touch_buttons_waking_stream,
215            ) = setup_button_relay(
216                kernel,
217                registry_proxy,
218                event_proxy_mode,
219                default_keyboard_device_opened_files,
220                default_keyboard_device_inspect,
221            );
222            let mut media_buttons_future = media_buttons_waking_stream.next();
223            let mut touch_buttons_future = touch_buttons_waking_stream.next();
224
225            let mut power_was_pressed = false;
226            let mut function_was_pressed = false;
227            let mut palm_was_pressed = false;
228
229            loop {
230                futures::select! {
231                    touch_future_res = touch_future => {
232                        match touch_future_res {
233                            Ok(touch_events) => {
234                                *previous_touch_event_disposition.borrow_mut() =
235                                    self.process_touch_event(
236                                        &mut default_touch_device,
237                                        touch_events,
238                                    );
239                                touch_future = touch_waking_proxy
240                                    .call(touch_waking_fn.clone())
241                                    .fuse();
242                            }
243                            Err(e) => {
244                                log_warn!(
245                                    "error {:?} reading from TouchSourceProxy; input is stopped",
246                                    e
247                                );
248                            }
249                        }
250                    }
251                    mouse_future_res = mouse_future => {
252                        match mouse_future_res {
253                            Ok(mouse_events) => {
254                                self.process_mouse_event(&mut default_mouse_device, mouse_events);
255                                mouse_future = mouse_waking_proxy
256                                    .call(fuipointer::MouseSourceProxy::watch)
257                                    .fuse();
258                            }
259                            Err(e) => {
260                                log_warn!(
261                                    "error {:?} reading from MouseSourceProxy; input is stopped",
262                                    e
263                                );
264                            }
265                        }
266                    }
267                    media_buttons_res = media_buttons_future => {
268                        match media_buttons_res {
269                            Some(Ok(event)) => {
270                                (power_was_pressed, function_was_pressed) =
271                                    self.process_media_button_event(
272                                        &mut default_button_device,
273                                        event,
274                                        power_was_pressed,
275                                        function_was_pressed,
276                                    );
277                            }
278                            _ => {}
279                        }
280                        media_buttons_future = media_buttons_waking_stream.next();
281                    }
282                    touch_buttons_res = touch_buttons_future => {
283                        match touch_buttons_res {
284                            Some(Ok(event)) => {
285                                palm_was_pressed = self.process_touch_button_event(
286                                    &mut default_touch_device,
287                                    event,
288                                    palm_was_pressed,
289                                );
290                            }
291                            _ => {}
292                        }
293                        touch_buttons_future = touch_buttons_waking_stream.next();
294                    }
295                    e = keyboard_event_stream.next() => {
296                        match e  {
297                            Some(Ok(request)) => {
298                                self.process_keyboard(&mut default_keyboard_device, request);
299                            }
300                            _ => {}
301                        }
302                    }
303                    e = self.receiver.next() => {
304                        match e {
305                            Some(event) => {
306                                match event {
307                                    DeviceStateChange::Add(id, device_state, sender) => {
308                                        self.devices.insert(id, device_state);
309                                        let _ = sender.send(());
310                                    }
311                                    DeviceStateChange::Remove(id, sender) => {
312                                        self.devices.remove(&id);
313                                        let _ = sender.send(());
314                                    }
315                                }
316                            }
317                            _ => {}
318                        }
319                    }
320                    complete => break,
321                }
322            }
323        };
324        let req = SpawnRequestBuilder::new()
325            .with_debug_name("input-event-relay")
326            .with_role(INPUT_RELAY_ROLE_NAME)
327            .with_async_closure(f)
328            .build();
329        kernel.kthreads.spawner().spawn_from_request(req);
330    }
331
332    fn process_touch_event(
333        self: &mut Self,
334        default_touch_device: &mut DeviceState,
335        touch_events: Vec<FidlTouchEvent>,
336    ) -> Vec<FidlTouchResponse> {
337        trace_duration!("input", "starnix_process_touch_event");
338        for e in &touch_events {
339            match e.trace_flow_id {
340                Some(trace_flow_id) => {
341                    trace_flow_end!("input", "dispatch_event_to_client", trace_flow_id.into());
342                }
343                None => {
344                    log_warn!("touch event has not tracing id");
345                }
346            }
347        }
348        let num_received_events: u64 = touch_events.len().try_into().unwrap();
349
350        let previous_event_disposition =
351            touch_events.iter().map(make_response_for_fidl_event).collect();
352
353        let mut num_ignored_events: u64 = 0;
354
355        // 1 vec may contains events from different device.
356        let (events_by_device, ignored_events) = group_touch_events_by_device_id(touch_events);
357        num_ignored_events += ignored_events;
358
359        for (device_id, events) in events_by_device {
360            trace_duration_begin!("input", "starnix_process_per_device_touch_event");
361
362            let dev = self.devices.get_mut(&device_id).unwrap_or(default_touch_device);
363
364            let mut num_converted_events: u64 = 0;
365            let mut num_unexpected_events: u64 = 0;
366            let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
367
368            let last_event_time_ns: i64;
369            if let InputDeviceType::Touch(ref mut converter) = dev.device_type {
370                let mut batch = converter.handle(events);
371                new_events.append(&mut batch.events);
372                num_converted_events += batch.count_converted_fidl_events;
373                num_ignored_events += batch.count_ignored_fidl_events;
374                num_unexpected_events += batch.count_unexpected_fidl_events;
375                last_event_time_ns = batch.last_event_time_ns;
376            } else {
377                trace_duration_end!("input", "starnix_process_per_device_touch_event");
378                log_warn!(
379                    "Non touch device received touch events: device_id = {}, device_type = {}",
380                    device_id,
381                    dev.device_type
382                );
383                continue;
384            }
385
386            if let Some(dev_inspect_status) = &dev.inspect_status {
387                dev_inspect_status.count_total_received_events(num_received_events);
388                dev_inspect_status.count_total_ignored_events(num_ignored_events);
389                dev_inspect_status.count_total_unexpected_events(num_unexpected_events);
390                dev_inspect_status.count_total_converted_events(num_converted_events);
391                dev_inspect_status.count_total_generated_events(
392                    new_events.len().try_into().unwrap(),
393                    last_event_time_ns,
394                );
395            } else {
396                log_warn!(
397                    "unable to record inspect for device_id: {}, device_type: {}",
398                    device_id,
399                    dev.device_type
400                );
401            }
402
403            trace_duration_end!("input", "starnix_process_per_device_touch_event");
404            dev.open_files.lock().retain(|f| {
405                let Some(file) = f.upgrade() else {
406                    log_warn!("Dropping input file for touch that failed to upgrade");
407                    return false;
408                };
409                match &file.inspect_status {
410                    Some(file_inspect_status) => {
411                        file_inspect_status.count_received_events(num_received_events);
412                        file_inspect_status.count_ignored_events(num_ignored_events);
413                        file_inspect_status.count_unexpected_events(num_unexpected_events);
414                        file_inspect_status.count_converted_events(num_converted_events);
415                    }
416                    None => {
417                        log_warn!("unable to record inspect within the input file")
418                    }
419                }
420                if !new_events.is_empty() {
421                    // TODO(https://fxbug.dev/42075438): Reading from an `InputFile` should
422                    // not provide access to events that occurred before the file was
423                    // opened.
424                    if let Some(file_inspect_status) = &file.inspect_status {
425                        file_inspect_status.count_generated_events(
426                            new_events.len().try_into().unwrap(),
427                            last_event_time_ns,
428                        );
429                    }
430                    let mut inner = file.inner.lock();
431                    inner
432                        .events
433                        .extend(new_events.clone().into_iter().map(LinuxEventWithTraceId::new));
434                    inner.waiters.notify_fd_events(FdEvents::POLLIN);
435                }
436
437                true
438            });
439        }
440
441        previous_event_disposition
442    }
443
444    fn process_keyboard(
445        self: &mut Self,
446        default_keyboard_device: &mut DeviceState,
447        request: KeyboardListenerRequest,
448    ) {
449        match request {
450            KeyboardListenerRequest::OnKeyEvent { event, responder } => {
451                trace_duration!("input", "starnix_process_keyboard_event");
452
453                let new_events = parse_fidl_keyboard_event_to_linux_input_event(
454                    &event,
455                    uinput::uinput_running(),
456                );
457
458                let dev = match event.device_id {
459                    Some(device_id) => {
460                        self.devices.get_mut(&device_id).unwrap_or(default_keyboard_device)
461                    }
462                    None => default_keyboard_device,
463                };
464
465                dev.open_files.lock().retain(|f| {
466                    let Some(file) = f.upgrade() else {
467                        log_warn!("Dropping input file for keyboard that failed to upgrade");
468                        return false;
469                    };
470                    let mut inner = file.inner.lock();
471
472                    if !new_events.is_empty() {
473                        inner
474                            .events
475                            .extend(new_events.clone().into_iter().map(LinuxEventWithTraceId::new));
476                        inner.waiters.notify_fd_events(FdEvents::POLLIN);
477                    }
478
479                    true
480                });
481
482                responder.send(KeyEventStatus::Handled).expect("");
483            }
484        }
485    }
486
487    fn process_media_button_event(
488        &mut self,
489        default_button_device: &mut DeviceState,
490        button_event: fuipolicy::MediaButtonsListenerRequest,
491        power_was_pressed: bool,
492        function_was_pressed: bool,
493    ) -> (bool, bool) {
494        let mut power_was_pressed_after = false;
495        let mut function_was_pressed_after = false;
496        match button_event {
497            fuipolicy::MediaButtonsListenerRequest::OnEvent { event, responder } => {
498                trace_duration!("input", "starnix_process_media_button_event");
499
500                let batch =
501                    parse_fidl_media_button_event(&event, power_was_pressed, function_was_pressed);
502
503                power_was_pressed_after = batch.power_is_pressed;
504                function_was_pressed_after = batch.function_is_pressed;
505
506                let (converted_events, ignored_events, generated_events) = match batch.events.len()
507                {
508                    0 => (0u64, 1u64, 0u64),
509                    len => {
510                        if len % 2 == 1 {
511                            log_warn!(
512                                "unexpectedly received {} events: there should always be an even number of non-empty events.",
513                                len
514                            );
515                        }
516                        (1u64, 0u64, len as u64)
517                    }
518                };
519
520                let dev = match event.device_id {
521                    Some(device_id) => {
522                        self.devices.get_mut(&device_id).unwrap_or(default_button_device)
523                    }
524                    None => default_button_device,
525                };
526
527                if let Some(dev_inspect_status) = &dev.inspect_status {
528                    dev_inspect_status.count_total_received_events(1);
529                    dev_inspect_status.count_total_ignored_events(ignored_events);
530                    dev_inspect_status.count_total_converted_events(converted_events);
531                    dev_inspect_status.count_total_generated_events(
532                        generated_events,
533                        batch.event_time.into_nanos().try_into().unwrap(),
534                    );
535                } else {
536                    log_warn!("unable to record inspect for button device");
537                }
538
539                dev.open_files.lock().retain(|f| {
540                    let Some(file) = f.upgrade() else {
541                        log_warn!("Dropping input file for buttons that failed to upgrade");
542                        return false;
543                    };
544                    match &file.inspect_status {
545                        Some(file_inspect_status) => {
546                            file_inspect_status.count_received_events(1);
547                            file_inspect_status.count_ignored_events(ignored_events);
548                            file_inspect_status.count_converted_events(converted_events);
549                        }
550                        None => {
551                            log_warn!("unable to record inspect within the input file")
552                        }
553                    }
554                    if !batch.events.is_empty() {
555                        if let Some(file_inspect_status) = &file.inspect_status {
556                            file_inspect_status.count_generated_events(
557                                generated_events,
558                                batch.event_time.into_nanos().try_into().unwrap(),
559                            );
560                        }
561                        let mut inner = file.inner.lock();
562                        inner.events.extend(
563                            batch.events.clone().into_iter().map(LinuxEventWithTraceId::new),
564                        );
565                        inner.waiters.notify_fd_events(FdEvents::POLLIN);
566                    }
567
568                    true
569                });
570
571                responder.send().expect("media buttons responder failed to respond");
572            }
573            _ => { /* Ignore deprecated OnMediaButtonsEvent */ }
574        }
575
576        (power_was_pressed_after, function_was_pressed_after)
577    }
578
579    fn process_touch_button_event(
580        &mut self,
581        default_touch_device: &mut DeviceState,
582        button_event: fuipolicy::TouchButtonsListenerRequest,
583        palm_was_pressed: bool,
584    ) -> bool {
585        trace_duration!("input", "starnix_process_touch_button_event");
586        match button_event {
587            fuipolicy::TouchButtonsListenerRequest::OnEvent { event, responder } => {
588                let batch = parse_fidl_touch_button_event(&event, palm_was_pressed);
589
590                let (converted_events, ignored_events, generated_events) = match batch.events.len()
591                {
592                    0 => (0u64, 1u64, 0u64),
593                    len => {
594                        if len % 2 == 1 {
595                            log_warn!(
596                                "unexpectedly received {} events: there should always be an even number of non-empty events.",
597                                len
598                            );
599                        }
600                        (1u64, 0u64, len as u64)
601                    }
602                };
603
604                let dev = match event.device_info {
605                    Some(TouchDeviceInfo { id: Some(device_id), .. }) => {
606                        self.devices.get_mut(&device_id).unwrap_or(default_touch_device)
607                    }
608                    _ => default_touch_device,
609                };
610
611                if let Some(dev_inspect_status) = &dev.inspect_status {
612                    dev_inspect_status.count_total_received_events(1);
613                    dev_inspect_status.count_total_ignored_events(ignored_events);
614                    dev_inspect_status.count_total_converted_events(converted_events);
615                    dev_inspect_status.count_total_generated_events(
616                        generated_events,
617                        batch.event_time.into_nanos().try_into().unwrap(),
618                    );
619                } else {
620                    log_warn!("unable to record inspect for touch device");
621                }
622
623                dev.open_files.lock().retain(|f| {
624                    let Some(file) = f.upgrade() else {
625                        log_warn!("Dropping input file for touch that failed to upgrade");
626                        return false;
627                    };
628                    match &file.inspect_status {
629                        Some(file_inspect_status) => {
630                            file_inspect_status.count_received_events(1);
631                            file_inspect_status.count_ignored_events(ignored_events);
632                            file_inspect_status.count_converted_events(converted_events);
633                        }
634                        None => {
635                            log_warn!("unable to record inspect within the input file")
636                        }
637                    }
638                    if !batch.events.is_empty() {
639                        if let Some(file_inspect_status) = &file.inspect_status {
640                            file_inspect_status.count_generated_events(
641                                generated_events,
642                                batch.event_time.into_nanos().try_into().unwrap(),
643                            );
644                        }
645                        let mut inner = file.inner.lock();
646                        inner.events.extend(
647                            batch.events.clone().into_iter().map(LinuxEventWithTraceId::new),
648                        );
649                        inner.waiters.notify_fd_events(FdEvents::POLLIN);
650                    }
651
652                    true
653                });
654
655                responder.send().expect("touch buttons responder failed to respond");
656
657                batch.palm_is_pressed
658            }
659            fuipolicy::TouchButtonsListenerRequest::_UnknownMethod { ordinal, .. } => {
660                log_warn!("Received an unknown method with ordinal {ordinal}");
661                palm_was_pressed
662            }
663        }
664    }
665
666    fn process_mouse_event(
667        self: &Self,
668        default_mouse_device: &mut DeviceState,
669        mouse_events: Vec<FidlMouseEvent>,
670    ) {
671        let num_received_events: u64 = mouse_events.len().try_into().unwrap();
672        let mut num_ignored_events: u64 = 0;
673        let mut num_converted_events: u64 = 0;
674        let mut num_unexpected_events: u64 = 0;
675        let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
676        let mut last_event_time_ns = zx::MonotonicInstant::get();
677        for event in mouse_events {
678            match event {
679                FidlMouseEvent {
680                    timestamp: Some(time),
681                    pointer_sample: Some(MousePointerSample { scroll_v: Some(ticks), .. }),
682                    ..
683                } => {
684                    last_event_time_ns = zx::MonotonicInstant::from_nanos(time);
685                    // Ensure this is a mouse wheel event with delta, otherwise ignore.
686                    if ticks != 0 {
687                        new_events.push_back(uapi::input_event {
688                            time: timeval_from_time(last_event_time_ns),
689                            type_: uapi::EV_REL as u16,
690                            code: uapi::REL_WHEEL as u16,
691                            value: ticks as i32,
692                        });
693                        num_converted_events += 1;
694                    } else {
695                        num_ignored_events += 1;
696                    }
697                }
698                _ => {
699                    num_unexpected_events += 1;
700                }
701            }
702        }
703        if new_events.len() > 0 {
704            new_events.push_back(uapi::input_event {
705                // See https://www.kernel.org/doc/Documentation/input/event-codes.rst.
706                time: timeval_from_time(last_event_time_ns),
707                type_: uapi::EV_SYN as u16,
708                code: uapi::SYN_REPORT as u16,
709                value: 0,
710            });
711        }
712
713        if let Some(dev_inspect_status) = &default_mouse_device.inspect_status {
714            dev_inspect_status.count_total_received_events(num_received_events);
715            dev_inspect_status.count_total_ignored_events(num_ignored_events);
716            dev_inspect_status.count_total_unexpected_events(num_unexpected_events);
717            dev_inspect_status.count_total_converted_events(num_converted_events);
718            if !new_events.is_empty() {
719                dev_inspect_status.count_total_generated_events(
720                    new_events.len().try_into().unwrap(),
721                    last_event_time_ns.into_nanos().try_into().unwrap(),
722                );
723            }
724        } else {
725            log_warn!("unable to record inspect for mouse device");
726        }
727
728        default_mouse_device.open_files.lock().retain(|f| {
729            let Some(file) = f.upgrade() else {
730                log_warn!("Dropping input file for mouse that failed to upgrade");
731                return false;
732            };
733            match &file.inspect_status {
734                Some(file_inspect_status) => {
735                    file_inspect_status.count_received_events(num_received_events);
736                    file_inspect_status.count_ignored_events(num_ignored_events);
737                    file_inspect_status.count_unexpected_events(num_unexpected_events);
738                    file_inspect_status.count_converted_events(num_converted_events);
739                }
740                None => {
741                    log_warn!("unable to record inspect within the input file")
742                }
743            }
744            if !new_events.is_empty() {
745                if let Some(file_inspect_status) = &file.inspect_status {
746                    file_inspect_status.count_generated_events(
747                        new_events.len().try_into().unwrap(),
748                        last_event_time_ns.into_nanos().try_into().unwrap(),
749                    );
750                }
751                let mut inner = file.inner.lock();
752                inner.events.extend(new_events.clone().into_iter().map(LinuxEventWithTraceId::new));
753                inner.waiters.notify_fd_events(FdEvents::POLLIN);
754            }
755            true
756        });
757    }
758}
759
760fn setup_touch_relay(
761    kernel: &Arc<Kernel>,
762    event_proxy_mode: EventProxyMode,
763    touch_source_client_end: ClientEnd<fuipointer::TouchSourceMarker>,
764    default_touch_device_opened_files: OpenedFiles,
765    device_inspect_status: Option<Arc<InputDeviceStatus>>,
766) -> (DeviceState, ContainerWakingProxy<fuipointer::TouchSourceProxy>) {
767    let touch_counter_name = "touch";
768    let default_touch_device = DeviceState {
769        device_type: InputDeviceType::Touch(FuchsiaTouchEventToLinuxTouchEventConverter::create()),
770        open_files: default_touch_device_opened_files,
771        inspect_status: device_inspect_status,
772    };
773    let (touch_source_proxy, counter) = match event_proxy_mode {
774        EventProxyMode::WakeContainer => {
775            // Proxy the touch events through the Starnix runner. This allows touch events to
776            // wake the container when it is suspended.
777            let (touch_source_channel, counter) = create_proxy_for_wake_events_counter(
778                touch_source_client_end.into_channel(),
779                touch_counter_name.to_string(),
780            );
781            (
782                fuipointer::TouchSourceProxy::new(fidl::AsyncChannel::from_channel(
783                    touch_source_channel,
784                )),
785                Some(counter),
786            )
787        }
788        EventProxyMode::None => (touch_source_client_end.into_proxy(), None),
789    };
790    (
791        default_touch_device,
792        ContainerWakingProxy::new(
793            kernel.suspend_resume_manager.add_message_counter(touch_counter_name, counter),
794            touch_source_proxy,
795        ),
796    )
797}
798
799fn setup_keyboard_relay(
800    keyboard: KeyboardSynchronousProxy,
801    view_ref: fuiviews::ViewRef,
802    default_keyboard_device_opened_files: OpenedFiles,
803    device_inspect_status: Option<Arc<InputDeviceStatus>>,
804) -> (DeviceState, KeyboardListenerRequestStream) {
805    let default_keyboard_device = DeviceState {
806        device_type: InputDeviceType::Keyboard,
807        open_files: default_keyboard_device_opened_files,
808        inspect_status: device_inspect_status,
809    };
810    let (keyboard_listener, event_stream) =
811        fidl::endpoints::create_request_stream::<KeyboardListenerMarker>();
812    if keyboard.add_listener(view_ref, keyboard_listener, zx::MonotonicInstant::INFINITE).is_err() {
813        log_warn!("Could not register keyboard listener");
814    }
815
816    (default_keyboard_device, event_stream)
817}
818
819fn setup_button_relay(
820    kernel: &Arc<Kernel>,
821    registry_proxy: fuipolicy::DeviceListenerRegistrySynchronousProxy,
822    event_proxy_mode: EventProxyMode,
823    default_keyboard_device_opened_files: OpenedFiles,
824    device_inspect_status: Option<Arc<InputDeviceStatus>>,
825) -> (
826    DeviceState,
827    ContainerWakingStream<fuipolicy::MediaButtonsListenerRequestStream>,
828    ContainerWakingStream<fuipolicy::TouchButtonsListenerRequestStream>,
829) {
830    let default_keyboard_device = DeviceState {
831        device_type: InputDeviceType::Keyboard,
832        open_files: default_keyboard_device_opened_files,
833        inspect_status: device_inspect_status,
834    };
835    let media_buttons_name = "media buttons";
836    let touch_buttons_name = "touch buttons";
837
838    let (remote_media_button_client, remote_media_button_server) =
839        fidl::endpoints::create_endpoints::<fuipolicy::MediaButtonsListenerMarker>();
840    if let Err(e) =
841        registry_proxy.register_listener(remote_media_button_client, zx::MonotonicInstant::INFINITE)
842    {
843        log_warn!("Failed to register media buttons listener: {:?}", e);
844    }
845
846    let (remote_touch_button_client, remote_touch_button_server) =
847        fidl::endpoints::create_endpoints::<fuipolicy::TouchButtonsListenerMarker>();
848    if let Err(e) = registry_proxy
849        .register_touch_buttons_listener(remote_touch_button_client, zx::MonotonicInstant::INFINITE)
850    {
851        log_warn!("Failed to register touch buttons listener: {:?}", e);
852    }
853
854    let (
855        local_media_buttons_listener_stream,
856        media_buttons_counter,
857        local_touch_buttons_listener_stream,
858        touch_buttons_counter,
859    ) = match event_proxy_mode {
860        EventProxyMode::WakeContainer => {
861            let (local_media_buttons_channel, media_buttons_counter) =
862                create_proxy_for_wake_events_counter(
863                    remote_media_button_server.into_channel(),
864                    media_buttons_name.to_string(),
865                );
866            let local_media_buttons_listener_stream =
867                fuipolicy::MediaButtonsListenerRequestStream::from_channel(
868                    fidl::AsyncChannel::from_channel(local_media_buttons_channel),
869                );
870
871            let (local_touch_buttons_channel, touch_buttons_counter) =
872                create_proxy_for_wake_events_counter(
873                    remote_touch_button_server.into_channel(),
874                    touch_buttons_name.to_string(),
875                );
876            let local_touch_buttons_listener_stream =
877                fuipolicy::TouchButtonsListenerRequestStream::from_channel(
878                    fidl::AsyncChannel::from_channel(local_touch_buttons_channel),
879                );
880            (
881                local_media_buttons_listener_stream,
882                Some(media_buttons_counter),
883                local_touch_buttons_listener_stream,
884                Some(touch_buttons_counter),
885            )
886        }
887        EventProxyMode::None => (
888            remote_media_button_server.into_stream(),
889            None,
890            remote_touch_button_server.into_stream(),
891            None,
892        ),
893    };
894
895    (
896        default_keyboard_device,
897        ContainerWakingStream::new(
898            kernel
899                .suspend_resume_manager
900                .add_message_counter(media_buttons_name, media_buttons_counter),
901            local_media_buttons_listener_stream,
902        ),
903        ContainerWakingStream::new(
904            kernel
905                .suspend_resume_manager
906                .add_message_counter(touch_buttons_name, touch_buttons_counter),
907            local_touch_buttons_listener_stream,
908        ),
909    )
910}
911
912fn setup_mouse_relay(
913    kernel: &Arc<Kernel>,
914    event_proxy_mode: EventProxyMode,
915    mouse_source_client_end: ClientEnd<fuipointer::MouseSourceMarker>,
916    default_mouse_device_opened_files: OpenedFiles,
917    device_inspect_status: Option<Arc<InputDeviceStatus>>,
918) -> (DeviceState, ContainerWakingProxy<fuipointer::MouseSourceProxy>) {
919    let mouse_counter_name = "mouse";
920    let default_mouse_device = DeviceState {
921        device_type: InputDeviceType::Mouse,
922        open_files: default_mouse_device_opened_files,
923        inspect_status: device_inspect_status,
924    };
925    let (mouse_source_proxy, counter) = match event_proxy_mode {
926        EventProxyMode::WakeContainer => {
927            // Proxy the mouse events through the Starnix runner. This allows mouse events to
928            // wake the container when it is suspended.
929            let (mouse_source_channel, resume_event) = create_proxy_for_wake_events_counter(
930                mouse_source_client_end.into_channel(),
931                "mouse".to_string(),
932            );
933            (
934                fuipointer::MouseSourceProxy::new(fidl::AsyncChannel::from_channel(
935                    mouse_source_channel,
936                )),
937                Some(resume_event),
938            )
939        }
940        EventProxyMode::None => (mouse_source_client_end.into_proxy(), None),
941    };
942
943    (
944        default_mouse_device,
945        ContainerWakingProxy::new(
946            kernel.suspend_resume_manager.add_message_counter(mouse_counter_name, counter),
947            mouse_source_proxy,
948        ),
949    )
950}
951
952/// Returns a FIDL response for `fidl_event`.
953fn make_response_for_fidl_event(fidl_event: &FidlTouchEvent) -> FidlTouchResponse {
954    match fidl_event {
955        FidlTouchEvent { pointer_sample: Some(_), .. } => FidlTouchResponse {
956            response_type: Some(TouchResponseType::Yes), // Event consumed by Starnix.
957            trace_flow_id: fidl_event.trace_flow_id,
958            ..Default::default()
959        },
960        _ => FidlTouchResponse::default(),
961    }
962}
963
964fn group_touch_events_by_device_id(
965    events: Vec<FidlTouchEvent>,
966) -> (HashMap<DeviceId, Vec<FidlTouchEvent>>, u64) {
967    let mut events_by_device: HashMap<u32, Vec<FidlTouchEvent>> = HashMap::new();
968    let mut ignored_events: u64 = 0;
969    for e in events {
970        match e {
971            FidlTouchEvent {
972                pointer_sample: Some(TouchPointerSample { interaction: Some(id), .. }),
973                ..
974            } => {
975                events_by_device.entry(id.device_id).or_default().push(e);
976            }
977            _ => {
978                ignored_events += 1;
979            }
980        }
981    }
982
983    (events_by_device, ignored_events)
984}
985
986#[cfg(test)]
987pub async fn start_input_relays_for_test(
988    locked: &mut starnix_sync::Locked<starnix_sync::Unlocked>,
989    current_task: &starnix_core::task::CurrentTask,
990    event_proxy_mode: EventProxyMode,
991) -> (
992    Arc<InputEventsRelayHandle>,
993    crate::InputDevice,
994    crate::InputDevice,
995    crate::InputDevice,
996    starnix_core::vfs::FileHandle,
997    starnix_core::vfs::FileHandle,
998    starnix_core::vfs::FileHandle,
999    fuipointer::TouchSourceRequestStream,
1000    fuipointer::MouseSourceRequestStream,
1001    fidl_fuchsia_ui_input3::KeyboardListenerProxy,
1002    fuipolicy::MediaButtonsListenerProxy,
1003    fuipolicy::TouchButtonsListenerProxy,
1004) {
1005    let inspector = fuchsia_inspect::Inspector::default();
1006
1007    let touch_device = crate::InputDevice::new_touch(700, 1200, inspector.root());
1008    let touch_file =
1009        touch_device.open_test(locked, current_task).expect("Failed to create input file");
1010
1011    let keyboard_device = crate::InputDevice::new_keyboard(inspector.root());
1012    let keyboard_file =
1013        keyboard_device.open_test(locked, current_task).expect("Failed to create input file");
1014
1015    let mouse_device = crate::InputDevice::new_mouse(inspector.root());
1016    let mouse_file =
1017        mouse_device.open_test(locked, current_task).expect("Failed to create input file");
1018
1019    let (touch_source_client_end, touch_source_stream) =
1020        fidl::endpoints::create_request_stream::<fuipointer::TouchSourceMarker>();
1021    let (mouse_source_client_end, mouse_stream) =
1022        fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
1023    let (keyboard_proxy, mut keyboard_stream) =
1024        fidl::endpoints::create_sync_proxy_and_stream::<fidl_fuchsia_ui_input3::KeyboardMarker>();
1025    let view_ref_pair = fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
1026    let (device_registry_proxy, mut device_listener_stream) =
1027        fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>();
1028
1029    let (relay, relay_handle) = new_input_relay();
1030    relay.start_relays(
1031        &current_task.kernel(),
1032        event_proxy_mode,
1033        touch_source_client_end,
1034        keyboard_proxy,
1035        mouse_source_client_end,
1036        view_ref_pair.view_ref,
1037        device_registry_proxy,
1038        touch_device.open_files.clone(),
1039        keyboard_device.open_files.clone(),
1040        mouse_device.open_files.clone(),
1041        Some(touch_device.inspect_status.clone()),
1042        Some(keyboard_device.inspect_status.clone()),
1043        Some(mouse_device.inspect_status.clone()),
1044    );
1045
1046    let keyboard_listener = match keyboard_stream.next().await {
1047        Some(Ok(fidl_fuchsia_ui_input3::KeyboardRequest::AddListener {
1048            view_ref: _,
1049            listener,
1050            responder,
1051        })) => {
1052            let _ = responder.send();
1053            listener.into_proxy()
1054        }
1055        _ => {
1056            panic!("Failed to get event");
1057        }
1058    };
1059
1060    let media_buttons_listener = match device_listener_stream.next().await {
1061        Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
1062            listener,
1063            responder,
1064        })) => {
1065            let _ = responder.send();
1066            listener.into_proxy()
1067        }
1068        _ => {
1069            panic!("Failed to get event");
1070        }
1071    };
1072
1073    let touch_buttons_listener = match device_listener_stream.next().await {
1074        Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
1075            listener,
1076            responder,
1077        })) => {
1078            let _ = responder.send();
1079            listener.into_proxy()
1080        }
1081        _ => {
1082            panic!("Failed to get event");
1083        }
1084    };
1085
1086    (
1087        relay_handle,
1088        touch_device,
1089        keyboard_device,
1090        mouse_device,
1091        touch_file,
1092        keyboard_file,
1093        mouse_file,
1094        touch_source_stream,
1095        mouse_stream,
1096        keyboard_listener,
1097        media_buttons_listener,
1098        touch_buttons_listener,
1099    )
1100}
1101
1102#[cfg(test)]
1103mod test {
1104    use super::*;
1105    use anyhow::anyhow;
1106    use fidl_fuchsia_ui_input::{
1107        MediaButtonsEvent, TouchButton, TouchButtonsEvent, TouchDeviceInfo,
1108    };
1109    use fidl_fuchsia_ui_input3 as fuiinput;
1110    use fuipointer::{
1111        EventPhase, MouseEvent, TouchEvent, TouchInteractionId, TouchPointerSample, TouchResponse,
1112        TouchSourceRequest, TouchSourceRequestStream,
1113    };
1114    use starnix_core::task::CurrentTask;
1115    #[allow(deprecated, reason = "pre-existing usage")]
1116    use starnix_core::testing::create_kernel_task_and_unlocked;
1117    use starnix_core::vfs::{FileHandle, FileObject, VecOutputBuffer};
1118    use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Unlocked};
1119    use starnix_types::time::timeval_from_time;
1120    use starnix_uapi::errors::{EAGAIN, Errno};
1121    use starnix_uapi::input_id;
1122    use starnix_uapi::open_flags::OpenFlags;
1123    use zerocopy::FromBytes as _;
1124
1125    const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
1126
1127    // Waits for a `Watch()` request to arrive on `request_stream`, and responds with
1128    // `touch_event`. Returns the arguments to the `Watch()` call.
1129    async fn answer_next_touch_watch_request(
1130        request_stream: &mut TouchSourceRequestStream,
1131        touch_events: Vec<TouchEvent>,
1132    ) -> Vec<TouchResponse> {
1133        match request_stream.next().await {
1134            Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
1135                responder.send(touch_events).expect("failure sending Watch reply");
1136                responses
1137            }
1138            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1139        }
1140    }
1141
1142    // Waits for a `Watch()` request to arrive on `request_stream`, and responds with
1143    // `mouse_events`.
1144    async fn answer_next_mouse_watch_request(
1145        request_stream: &mut fuipointer::MouseSourceRequestStream,
1146        mouse_events: Vec<MouseEvent>,
1147    ) {
1148        match request_stream.next().await {
1149            Some(Ok(fuipointer::MouseSourceRequest::Watch { responder })) => {
1150                responder.send(mouse_events).expect("failure sending Watch reply");
1151            }
1152            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1153        }
1154    }
1155
1156    fn make_empty_touch_event(device_id: u32) -> TouchEvent {
1157        TouchEvent {
1158            pointer_sample: Some(TouchPointerSample {
1159                interaction: Some(TouchInteractionId {
1160                    pointer_id: 0,
1161                    device_id,
1162                    interaction_id: 0,
1163                }),
1164                ..Default::default()
1165            }),
1166            ..Default::default()
1167        }
1168    }
1169
1170    fn make_touch_event_with_phase_device_id(
1171        phase: EventPhase,
1172        pointer_id: u32,
1173        device_id: u32,
1174    ) -> TouchEvent {
1175        make_touch_event_with_phase_device_id_position(phase, pointer_id, device_id, 0.0, 0.0)
1176    }
1177
1178    fn make_touch_event_with_phase_device_id_position(
1179        phase: EventPhase,
1180        pointer_id: u32,
1181        device_id: u32,
1182        x: f32,
1183        y: f32,
1184    ) -> TouchEvent {
1185        TouchEvent {
1186            timestamp: Some(0),
1187            pointer_sample: Some(TouchPointerSample {
1188                position_in_viewport: Some([x, y]),
1189                phase: Some(phase),
1190                interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
1191                ..Default::default()
1192            }),
1193            ..Default::default()
1194        }
1195    }
1196
1197    fn make_mouse_wheel_event(scroll_v_ticks: i64, device_id: u32) -> MouseEvent {
1198        MouseEvent {
1199            timestamp: Some(0),
1200            pointer_sample: Some(MousePointerSample {
1201                device_id: Some(device_id),
1202                scroll_v: Some(scroll_v_ticks),
1203                ..Default::default()
1204            }),
1205            ..Default::default()
1206        }
1207    }
1208
1209    fn read_uapi_events<L>(
1210        locked: &mut Locked<L>,
1211        file: &FileHandle,
1212        current_task: &CurrentTask,
1213    ) -> Vec<uapi::input_event>
1214    where
1215        L: LockEqualOrBefore<FileOpsCore>,
1216    {
1217        std::iter::from_fn(|| {
1218            let locked = locked.cast_locked::<FileOpsCore>();
1219            let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
1220            match file.read(locked, current_task, &mut event_bytes) {
1221                Ok(INPUT_EVENT_SIZE) => Some(
1222                    uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
1223                        .map_err(|_| anyhow!("failed to read input_event from buffer")),
1224                ),
1225                Ok(other_size) => {
1226                    Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
1227                }
1228                Err(Errno { code: EAGAIN, .. }) => None,
1229                Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
1230            }
1231        })
1232        .enumerate()
1233        .map(|(i, read_res)| match read_res {
1234            Ok(event) => event,
1235            Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
1236        })
1237        .collect()
1238    }
1239
1240    fn create_test_touch_device(
1241        locked: &mut Locked<Unlocked>,
1242        current_task: &CurrentTask,
1243        input_relay: Arc<InputEventsRelayHandle>,
1244        device_id: u32,
1245    ) -> FileHandle {
1246        let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1247        input_relay.add_touch_device(device_id, open_files.clone(), None);
1248        let device_file = Arc::new(InputFile::new_touch(
1249            input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1250            1000,
1251            1000,
1252            None,
1253        ));
1254        open_files.lock().push(Arc::downgrade(&device_file));
1255
1256        let root_namespace_node = current_task
1257            .lookup_path_from_root(locked, ".".into())
1258            .expect("failed to get namespace node for root");
1259
1260        FileObject::new(
1261            locked,
1262            &current_task,
1263            Box::new(device_file),
1264            root_namespace_node,
1265            OpenFlags::empty(),
1266        )
1267        .expect("FileObject::new failed")
1268    }
1269
1270    fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1271        uapi::input_event {
1272            time: timeval_from_time(zx::MonotonicInstant::from_nanos(0)),
1273            type_: ty as u16,
1274            code: code as u16,
1275            value,
1276        }
1277    }
1278
1279    #[::fuchsia::test]
1280    async fn route_touch_event_by_device_id() {
1281        // Set up resources.
1282        #[allow(deprecated, reason = "pre-existing usage")]
1283        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1284        let (
1285            input_relay,
1286            _touch_device,
1287            _keyboard_device,
1288            _mouse_device,
1289            input_file,
1290            _keyboard_file,
1291            _mouse_file,
1292            mut touch_source_stream,
1293            _mouse_source_stream,
1294            _keyboard_listener,
1295            _media_buttons_listener,
1296            _touch_buttons_listener,
1297        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1298
1299        const DEVICE_ID: u32 = 10;
1300
1301        answer_next_touch_watch_request(
1302            &mut touch_source_stream,
1303            vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1304        )
1305        .await;
1306
1307        // Wait for another `Watch` to ensure input_file done processing the first reply.
1308        // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1309        // `uapi::input_event`s.
1310        answer_next_touch_watch_request(
1311            &mut touch_source_stream,
1312            vec![make_empty_touch_event(DEVICE_ID)],
1313        )
1314        .await;
1315
1316        // Consume all of the `uapi::input_event`s that are available.
1317        let events = read_uapi_events(locked, &input_file, &current_task);
1318        // Default device receive events because no matched device.
1319        assert_ne!(events.len(), 0);
1320
1321        // add a device, mock uinput.
1322        let device_id_10_file =
1323            create_test_touch_device(locked, &current_task, input_relay.clone(), DEVICE_ID);
1324
1325        answer_next_touch_watch_request(
1326            &mut touch_source_stream,
1327            vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1328        )
1329        .await;
1330
1331        answer_next_touch_watch_request(
1332            &mut touch_source_stream,
1333            vec![make_empty_touch_event(DEVICE_ID)],
1334        )
1335        .await;
1336
1337        let events = read_uapi_events(locked, &input_file, &current_task);
1338        // Default device should not receive events because they matched device id 10.
1339        assert_eq!(events.len(), 0);
1340
1341        let events = read_uapi_events(locked, &device_id_10_file, &current_task);
1342        // file of device id 10 should receive events.
1343        assert_ne!(events.len(), 0);
1344    }
1345
1346    #[::fuchsia::test]
1347    async fn route_touch_event_by_device_id_multi_device_events_in_one_sequence() {
1348        #[allow(deprecated, reason = "pre-existing usage")]
1349        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1350        let (
1351            input_relay,
1352            _touch_device,
1353            _keyboard_device,
1354            _mouse_device,
1355            _input_file,
1356            _keyboard_file,
1357            _mouse_file,
1358            mut touch_source_stream,
1359            _mouse_source_stream,
1360            _keyboard_listener,
1361            _media_buttons_listener,
1362            _touch_buttons_listener,
1363        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1364
1365        const DEVICE_ID_10: u32 = 10;
1366        const DEVICE_ID_11: u32 = 11;
1367
1368        let device_id_10_file =
1369            create_test_touch_device(locked, &current_task, input_relay.clone(), DEVICE_ID_10);
1370
1371        let device_id_11_file =
1372            create_test_touch_device(locked, &current_task, input_relay.clone(), DEVICE_ID_11);
1373
1374        // 2 pointer down on different touch device.
1375        answer_next_touch_watch_request(
1376            &mut touch_source_stream,
1377            vec![
1378                make_touch_event_with_phase_device_id_position(
1379                    EventPhase::Add,
1380                    1,
1381                    DEVICE_ID_10,
1382                    10.0,
1383                    20.0,
1384                ),
1385                make_touch_event_with_phase_device_id_position(
1386                    EventPhase::Add,
1387                    2,
1388                    DEVICE_ID_11,
1389                    30.0,
1390                    40.0,
1391                ),
1392            ],
1393        )
1394        .await;
1395
1396        answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
1397
1398        let events_10 = read_uapi_events(locked, &device_id_10_file, &current_task);
1399        let events_11 = read_uapi_events(locked, &device_id_11_file, &current_task);
1400        assert_eq!(events_10.len(), events_11.len());
1401
1402        assert_eq!(
1403            events_10,
1404            vec![
1405                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1406                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1407                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1408                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1409                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1410                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1411            ]
1412        );
1413
1414        assert_eq!(
1415            events_11,
1416            vec![
1417                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1418                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1419                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 30),
1420                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 40),
1421                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1422                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1423            ]
1424        );
1425    }
1426
1427    #[::fuchsia::test]
1428    async fn route_key_event_by_device_id() {
1429        // Set up resources.
1430        #[allow(deprecated, reason = "pre-existing usage")]
1431        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1432        let (
1433            input_relay,
1434            _touch_device,
1435            _keyboard_device,
1436            _mouse_device,
1437            _touch_file,
1438            keyboard_file,
1439            _mouse_file,
1440            _touch_source_stream,
1441            _mouse_source_stream,
1442            keyboard_listener,
1443            _media_buttons_listener,
1444            _touch_buttons_listener,
1445        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1446
1447        const DEVICE_ID: u32 = 10;
1448
1449        let key_event = fuiinput::KeyEvent {
1450            timestamp: Some(0),
1451            type_: Some(fuiinput::KeyEventType::Pressed),
1452            key: Some(fidl_fuchsia_input::Key::A),
1453            device_id: Some(DEVICE_ID),
1454            ..Default::default()
1455        };
1456
1457        let _ = keyboard_listener.on_key_event(&key_event).await;
1458
1459        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1460        // Default device should receive events because no device device id is 10.
1461        assert_ne!(events.len(), 0);
1462
1463        // add a device, mock uinput.
1464        let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1465        input_relay.add_keyboard_device(DEVICE_ID, open_files.clone(), None);
1466        let device_id_10_file = Arc::new(InputFile::new_keyboard(
1467            input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1468            None,
1469        ));
1470        open_files.lock().push(Arc::downgrade(&device_id_10_file));
1471        let root_namespace_node = current_task
1472            .lookup_path_from_root(locked, ".".into())
1473            .expect("failed to get namespace node for root");
1474        let device_id_10_file_object = FileObject::new(
1475            locked,
1476            &current_task,
1477            Box::new(device_id_10_file),
1478            root_namespace_node,
1479            OpenFlags::empty(),
1480        )
1481        .expect("FileObject::new failed");
1482
1483        let _ = keyboard_listener.on_key_event(&key_event).await;
1484
1485        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1486        // Default device should not receive events because they matched device id 10.
1487        assert_eq!(events.len(), 0);
1488
1489        let events = read_uapi_events(locked, &device_id_10_file_object, &current_task);
1490        // file of device id 10 should receive events.
1491        assert_ne!(events.len(), 0);
1492
1493        std::mem::drop(keyboard_listener); // Close Zircon channel.
1494    }
1495
1496    #[::fuchsia::test]
1497    async fn route_media_button_event_by_device_id() {
1498        // Set up resources.
1499        #[allow(deprecated, reason = "pre-existing usage")]
1500        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1501        let (
1502            input_relay,
1503            _touch_device,
1504            _keyboard_device,
1505            _mouse_device,
1506            _touch_file,
1507            keyboard_file,
1508            _mouse_file,
1509            _touch_source_stream,
1510            _mouse_source_stream,
1511            _keyboard_listener,
1512            media_buttons_listener,
1513            _touch_buttons_listener,
1514        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1515
1516        const DEVICE_ID: u32 = 10;
1517
1518        let power_pressed_event = MediaButtonsEvent {
1519            volume: Some(0),
1520            mic_mute: Some(false),
1521            pause: Some(false),
1522            camera_disable: Some(false),
1523            power: Some(true),
1524            function: Some(false),
1525            device_id: Some(DEVICE_ID),
1526            ..Default::default()
1527        };
1528
1529        let _ = media_buttons_listener.on_event(power_pressed_event).await;
1530
1531        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1532        // Default device should receive events because no device device id is 10.
1533        assert_ne!(events.len(), 0);
1534
1535        // add a device, mock uinput.
1536        let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1537        input_relay.add_keyboard_device(DEVICE_ID, open_files.clone(), None);
1538        let device_id_10_file = Arc::new(InputFile::new_keyboard(
1539            input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1540            None,
1541        ));
1542        open_files.lock().push(Arc::downgrade(&device_id_10_file));
1543        let root_namespace_node = current_task
1544            .lookup_path_from_root(locked, ".".into())
1545            .expect("failed to get namespace node for root");
1546        let device_id_10_file_object = FileObject::new(
1547            locked,
1548            &current_task,
1549            Box::new(device_id_10_file),
1550            root_namespace_node,
1551            OpenFlags::empty(),
1552        )
1553        .expect("FileObject::new failed");
1554
1555        let power_released_event = MediaButtonsEvent {
1556            volume: Some(0),
1557            mic_mute: Some(false),
1558            pause: Some(false),
1559            camera_disable: Some(false),
1560            power: Some(false),
1561            function: Some(false),
1562            device_id: Some(DEVICE_ID),
1563            ..Default::default()
1564        };
1565
1566        let _ = media_buttons_listener.on_event(power_released_event).await;
1567
1568        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1569        // Default device should not receive events because they matched device id 10.
1570        assert_eq!(events.len(), 0);
1571
1572        let events = read_uapi_events(locked, &device_id_10_file_object, &current_task);
1573        // file of device id 10 should receive events.
1574        assert_ne!(events.len(), 0);
1575
1576        std::mem::drop(media_buttons_listener); // Close Zircon channel.
1577    }
1578
1579    #[::fuchsia::test]
1580    async fn route_touch_button_event_by_device_id() {
1581        // Set up resources.
1582        #[allow(deprecated, reason = "pre-existing usage")]
1583        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1584        let (
1585            input_relay,
1586            _touch_device,
1587            _keyboard_device,
1588            _mouse_device,
1589            touch_file,
1590            _keyboard_file,
1591            _mouse_file,
1592            _touch_source_stream,
1593            _mouse_source_stream,
1594            _keyboard_listener,
1595            _media_buttons_listener,
1596            touch_buttons_listener,
1597        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1598
1599        const DEVICE_ID: u32 = 10;
1600
1601        let palm_pressed_event: TouchButtonsEvent = TouchButtonsEvent {
1602            pressed_buttons: Some(vec![TouchButton::Palm]),
1603            device_info: Some(TouchDeviceInfo { id: Some(DEVICE_ID), ..Default::default() }),
1604            ..Default::default()
1605        };
1606
1607        let _ = touch_buttons_listener.on_event(palm_pressed_event).await;
1608
1609        let events = read_uapi_events(locked, &touch_file, &current_task);
1610        // Default device should receive events because no device device id is 10.
1611        assert_ne!(events.len(), 0);
1612
1613        // add a device, mock uinput.
1614        let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1615        input_relay.add_touch_device(DEVICE_ID, open_files.clone(), None);
1616        let device_id_10_file =
1617            create_test_touch_device(locked, &current_task, input_relay.clone(), DEVICE_ID);
1618
1619        let power_released_event: TouchButtonsEvent = TouchButtonsEvent {
1620            pressed_buttons: Some(vec![]),
1621            device_info: Some(TouchDeviceInfo { id: Some(DEVICE_ID), ..Default::default() }),
1622            ..Default::default()
1623        };
1624
1625        let _ = touch_buttons_listener.on_event(power_released_event).await;
1626
1627        let events = read_uapi_events(locked, &touch_file, &current_task);
1628        // Default device should not receive events because they matched device id 10.
1629        assert_eq!(events.len(), 0);
1630
1631        let events = read_uapi_events(locked, &device_id_10_file, &current_task);
1632        // file of device id 10 should receive events.
1633        assert_ne!(events.len(), 0);
1634
1635        std::mem::drop(touch_buttons_listener); // Close Zircon channel.
1636    }
1637
1638    #[::fuchsia::test]
1639    async fn touch_device_multi_reader() {
1640        // Set up resources.
1641        #[allow(deprecated, reason = "pre-existing usage")]
1642        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1643        let (
1644            _input_relay,
1645            touch_device,
1646            _keyboard_device,
1647            _mouse_device,
1648            touch_reader1,
1649            _keyboard_file,
1650            _mouse_file,
1651            mut touch_source_stream,
1652            _mouse_source_stream,
1653            _keyboard_listener,
1654            _media_buttons_listener,
1655            _touch_buttons_listener,
1656        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1657
1658        let touch_reader2 =
1659            touch_device.open_test(locked, &current_task).expect("Failed to create input file");
1660
1661        const DEVICE_ID: u32 = 10;
1662
1663        answer_next_touch_watch_request(
1664            &mut touch_source_stream,
1665            vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1666        )
1667        .await;
1668
1669        // Wait for another `Watch` to ensure input_file done processing the first reply.
1670        // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1671        // `uapi::input_event`s.
1672        answer_next_touch_watch_request(
1673            &mut touch_source_stream,
1674            vec![make_empty_touch_event(DEVICE_ID)],
1675        )
1676        .await;
1677
1678        // Consume all of the `uapi::input_event`s that are available.
1679        let events_from_reader1 = read_uapi_events(locked, &touch_reader1, &current_task);
1680        let events_from_reader2 = read_uapi_events(locked, &touch_reader2, &current_task);
1681        assert_ne!(events_from_reader1.len(), 0);
1682        assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1683    }
1684
1685    #[::fuchsia::test]
1686    async fn keyboard_device_multi_reader() {
1687        // Set up resources.
1688        #[allow(deprecated, reason = "pre-existing usage")]
1689        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1690        let (
1691            _input_relay,
1692            _touch_device,
1693            keyboard_device,
1694            _mouse_device,
1695            _touch_file,
1696            keyboard_reader1,
1697            _mouse_file,
1698            _touch_source_stream,
1699            _mouse_source_stream,
1700            keyboard_listener,
1701            _media_buttons_listener,
1702            _touch_buttons_listener,
1703        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1704
1705        let keyboard_reader2 =
1706            keyboard_device.open_test(locked, &current_task).expect("Failed to create input file");
1707
1708        const DEVICE_ID: u32 = 10;
1709
1710        let key_event = fuiinput::KeyEvent {
1711            timestamp: Some(0),
1712            type_: Some(fuiinput::KeyEventType::Pressed),
1713            key: Some(fidl_fuchsia_input::Key::A),
1714            device_id: Some(DEVICE_ID),
1715            ..Default::default()
1716        };
1717
1718        let _ = keyboard_listener.on_key_event(&key_event).await;
1719
1720        // Consume all of the `uapi::input_event`s that are available.
1721        let events_from_reader1 = read_uapi_events(locked, &keyboard_reader1, &current_task);
1722        let events_from_reader2 = read_uapi_events(locked, &keyboard_reader2, &current_task);
1723        assert_ne!(events_from_reader1.len(), 0);
1724        assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1725    }
1726
1727    #[::fuchsia::test]
1728    async fn button_device_multi_reader() {
1729        // Set up resources.
1730        #[allow(deprecated, reason = "pre-existing usage")]
1731        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1732        let (
1733            _input_relay,
1734            _touch_device,
1735            keyboard_device,
1736            _mouse_device,
1737            _touch_file,
1738            keyboard_reader1,
1739            _mouse_file,
1740            _touch_source_stream,
1741            _mouse_source_stream,
1742            _keyboard_listener,
1743            media_buttons_listener,
1744            _touch_buttons_listener,
1745        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1746
1747        let keyboard_reader2 =
1748            keyboard_device.open_test(locked, &current_task).expect("Failed to create input file");
1749
1750        const DEVICE_ID: u32 = 10;
1751
1752        let power_pressed_event = MediaButtonsEvent {
1753            volume: Some(0),
1754            mic_mute: Some(false),
1755            pause: Some(false),
1756            camera_disable: Some(false),
1757            power: Some(true),
1758            function: Some(false),
1759            device_id: Some(DEVICE_ID),
1760            ..Default::default()
1761        };
1762
1763        let _ = media_buttons_listener.on_event(power_pressed_event).await;
1764
1765        // Consume all of the `uapi::input_event`s that are available.
1766        let events_from_reader1 = read_uapi_events(locked, &keyboard_reader1, &current_task);
1767        let events_from_reader2 = read_uapi_events(locked, &keyboard_reader2, &current_task);
1768        assert_ne!(events_from_reader1.len(), 0);
1769        assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1770    }
1771
1772    #[::fuchsia::test]
1773    async fn mouse_device_multi_reader() {
1774        // Set up resources.
1775        #[allow(deprecated, reason = "pre-existing usage")]
1776        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1777        let (
1778            _input_relay,
1779            _touch_device,
1780            _keyboard_device,
1781            mouse_device,
1782            _touch_file,
1783            _keyboard_file,
1784            mouse_reader1,
1785            _touch_stream,
1786            mut mouse_stream,
1787            _keyboard_listener,
1788            _media_buttons_listener,
1789            _touch_buttons_listener,
1790        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::None).await;
1791
1792        let mouse_reader2 =
1793            mouse_device.open_test(locked, &current_task).expect("Failed to create input file");
1794
1795        const DEVICE_ID: u32 = 10;
1796
1797        answer_next_mouse_watch_request(
1798            &mut mouse_stream,
1799            vec![make_mouse_wheel_event(1, DEVICE_ID)],
1800        )
1801        .await;
1802
1803        // Wait for another `Watch` to ensure input_file done processing the first reply.
1804        // Use an empty `MouseEvent`, to minimize the chance that this event creates unexpected
1805        // `uapi::input_event`s.
1806        answer_next_mouse_watch_request(
1807            &mut mouse_stream,
1808            vec![make_mouse_wheel_event(0, DEVICE_ID)],
1809        )
1810        .await;
1811
1812        // Consume all of the `uapi::input_event`s that are available.
1813        let events_from_reader1 = read_uapi_events(locked, &mouse_reader1, &current_task);
1814        let events_from_reader2 = read_uapi_events(locked, &mouse_reader2, &current_task);
1815        assert_ne!(events_from_reader1.len(), 0);
1816        assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1817    }
1818
1819    #[::fuchsia::test]
1820    async fn input_message_counters() {
1821        // Set up resources.
1822        #[allow(deprecated, reason = "pre-existing usage")]
1823        let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
1824        let (
1825            _input_relay,
1826            _touch_device,
1827            _keyboard_device,
1828            _mouse_device,
1829            _touch_file,
1830            keyboard_file,
1831            _mouse_file,
1832            _touch_source_stream,
1833            _mouse_source_stream,
1834            keyboard_listener,
1835            _media_buttons_listener,
1836            _touch_buttons_listener,
1837        ) = start_input_relays_for_test(locked, &current_task, EventProxyMode::WakeContainer).await;
1838
1839        const DEVICE_ID: u32 = 10;
1840
1841        let key_event = fuiinput::KeyEvent {
1842            timestamp: Some(0),
1843            type_: Some(fuiinput::KeyEventType::Pressed),
1844            key: Some(fidl_fuchsia_input::Key::A),
1845            device_id: Some(DEVICE_ID),
1846            ..Default::default()
1847        };
1848
1849        let _ = keyboard_listener.on_key_event(&key_event).await;
1850
1851        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1852        assert_ne!(events.len(), 0);
1853
1854        assert!(!kernel.suspend_resume_manager.has_nonzero_message_counter());
1855    }
1856}