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