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