Skip to main content

starnix_modules_input/
input_device.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::InputFile;
6use crate::input_event_relay::{DeviceId, OpenedFiles};
7use fuchsia_inspect::{NumericProperty, Property};
8use starnix_core::device::kobject::DeviceMetadata;
9use starnix_core::device::{DeviceMode, DeviceOps};
10use starnix_core::task::CurrentTask;
11use starnix_core::vfs::{FileOps, FsString, NamespaceNode};
12#[cfg(test)]
13use starnix_sync::Unlocked;
14use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex};
15use starnix_uapi::device_type::{DeviceType, INPUT_MAJOR};
16use starnix_uapi::errors::Errno;
17use starnix_uapi::open_flags::OpenFlags;
18use starnix_uapi::{BUS_VIRTUAL, input_id};
19use std::sync::Arc;
20
21// Add a fuchsia-specific vendor ID. 0xfc1a is currently not allocated
22// to any vendor in the USB spec.
23//
24// May not be zero, see below.
25const FUCHSIA_VENDOR_ID: u16 = 0xfc1a;
26
27// May not be zero, see below.
28const FUCHSIA_TOUCH_PRODUCT_ID: u16 = 0x2;
29
30// May not be zero, see below.
31const FUCHSIA_KEYBOARD_PRODUCT_ID: u16 = 0x1;
32
33// May not be zero, see below.
34const FUCHSIA_MOUSE_PRODUCT_ID: u16 = 0x3;
35
36// Touch, keyboard, and mouse input IDs should be distinct.
37// Per https://www.linuxjournal.com/article/6429, the bus type should be populated with a
38// sensible value, but other fields may not be.
39//
40// While this may be the case for Linux itself, Android is not so relaxed.
41// Devices with apparently-invalid vendor or product IDs don't get extra
42// device configuration.  So we must make a minimum effort to present
43// sensibly-looking product and vendor IDs.  Zero version only means that
44// version-specific config files will not be applied.
45//
46// For background, see:
47//
48// * Allowable file locations:
49//   https://source.android.com/docs/core/interaction/input/input-device-configuration-files#location
50// * Android configuration selection code:
51//   https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:frameworks/native/libs/input/InputDevice.cpp;l=60;drc=285211e60bff87fc5a9c9b4105a4b4ccb7edffaf
52const TOUCH_INPUT_ID: input_id = input_id {
53    bustype: BUS_VIRTUAL as u16,
54    // Make sure that vendor ID and product ID at least seem plausible.  See
55    // above for details.
56    vendor: FUCHSIA_VENDOR_ID,
57    product: FUCHSIA_TOUCH_PRODUCT_ID,
58    // Version is OK to be zero, but config files named `Product_yyyy_Vendor_zzzz_Version_ttt.*`
59    // will not work.
60    version: 0,
61};
62const KEYBOARD_INPUT_ID: input_id = input_id {
63    bustype: BUS_VIRTUAL as u16,
64    // Make sure that vendor ID and product ID at least seem plausible.  See
65    // above for details.
66    vendor: FUCHSIA_VENDOR_ID,
67    product: FUCHSIA_KEYBOARD_PRODUCT_ID,
68    version: 1,
69};
70
71const MOUSE_INPUT_ID: input_id = input_id {
72    bustype: BUS_VIRTUAL as u16,
73    // Make sure that vendor ID and product ID at least seem plausible.  See
74    // above for details.
75    vendor: FUCHSIA_VENDOR_ID,
76    product: FUCHSIA_MOUSE_PRODUCT_ID,
77    version: 1,
78};
79
80#[derive(Clone)]
81enum InputDeviceType {
82    // A touch device, containing (display width, display height).
83    Touch(i32, i32),
84
85    // A keyboard device.
86    Keyboard,
87
88    // A mouse device.
89    Mouse,
90}
91
92/// An [`InputDeviceStatus`] is tied to an [`InputDeviceBinding`] and provides properties
93/// detailing its Inspect status.
94/// We expect all (non-timestamp) properties' counts to equal the sum of that property from all
95/// files opened on that device. So for example, if a device had 3 separate input files opened, we
96/// would expect it's `total_fidl_events_received_count` to equal the sum of
97/// `fidl_events_received_count` from all 3 files and so forth.
98pub struct InputDeviceStatus {
99    /// A node that contains the state below.
100    pub node: fuchsia_inspect::Node,
101
102    /// Hold onto inspect nodes for files opened on this device, so that when these files are
103    /// closed, their inspect data is maintained.
104    pub file_nodes: Mutex<Vec<fuchsia_inspect::Node>>,
105
106    /// The number of FIDL events received by this device from Fuchsia input system.
107    ///
108    /// We expect:
109    /// total_fidl_events_received_count = total_fidl_events_ignored_count +
110    ///                                    total_fidl_events_unexpected_count +
111    ///                                    total_fidl_events_converted_count
112    /// otherwise starnix ignored events unexpectedly.
113    ///
114    /// total_fidl_events_unexpected_count should be 0, if not it hints issues from upstream of
115    /// ui stack.
116    pub total_fidl_events_received_count: fuchsia_inspect::UintProperty,
117
118    /// The number of FIDL events ignored by this device when attempting conversion to this
119    /// module’s representation of a TouchEvent.
120    pub total_fidl_events_ignored_count: fuchsia_inspect::UintProperty,
121
122    /// The unexpected number of FIDL events reached to this module should be filtered out
123    /// earlier in the UI stack.
124    /// It maybe unexpected format or unexpected order.
125    pub total_fidl_events_unexpected_count: fuchsia_inspect::UintProperty,
126
127    /// The number of FIDL events converted by this device to this module’s representation of
128    /// TouchEvent.
129    pub total_fidl_events_converted_count: fuchsia_inspect::UintProperty,
130
131    /// The number of uapi::input_events generated by this device from TouchEvents.
132    pub total_uapi_events_generated_count: fuchsia_inspect::UintProperty,
133
134    /// The event time of the last generated uapi::input_event by one of this device's InputFiles.
135    pub last_generated_uapi_event_timestamp_ns: fuchsia_inspect::IntProperty,
136}
137
138impl InputDeviceStatus {
139    fn new(node: fuchsia_inspect::Node) -> Self {
140        let total_fidl_events_received_count =
141            node.create_uint("total_fidl_events_received_count", 0);
142        let total_fidl_events_ignored_count =
143            node.create_uint("total_fidl_events_ignored_count", 0);
144        let total_fidl_events_unexpected_count =
145            node.create_uint("total_fidl_events_unexpected_count", 0);
146        let total_fidl_events_converted_count =
147            node.create_uint("total_fidl_events_converted_count", 0);
148        let total_uapi_events_generated_count =
149            node.create_uint("total_uapi_events_generated_count", 0);
150        let last_generated_uapi_event_timestamp_ns =
151            node.create_int("last_generated_uapi_event_timestamp_ns", 0);
152        Self {
153            node,
154            file_nodes: Mutex::new(vec![]),
155            total_fidl_events_received_count,
156            total_fidl_events_ignored_count,
157            total_fidl_events_unexpected_count,
158            total_fidl_events_converted_count,
159            total_uapi_events_generated_count,
160            last_generated_uapi_event_timestamp_ns,
161        }
162    }
163
164    pub fn count_total_received_events(&self, count: u64) {
165        self.total_fidl_events_received_count.add(count);
166    }
167
168    pub fn count_total_ignored_events(&self, count: u64) {
169        self.total_fidl_events_ignored_count.add(count);
170    }
171
172    pub fn count_total_unexpected_events(&self, count: u64) {
173        self.total_fidl_events_unexpected_count.add(count);
174    }
175
176    pub fn count_total_converted_events(&self, count: u64) {
177        self.total_fidl_events_converted_count.add(count);
178    }
179
180    pub fn count_total_generated_events(&self, count: u64, event_time_ns: i64) {
181        self.total_uapi_events_generated_count.add(count);
182        self.last_generated_uapi_event_timestamp_ns.set(event_time_ns);
183    }
184}
185
186#[derive(Clone)]
187pub struct InputDevice {
188    device_type: InputDeviceType,
189
190    pub open_files: OpenedFiles,
191
192    pub inspect_status: Arc<InputDeviceStatus>,
193}
194
195impl InputDevice {
196    pub fn new_touch(
197        display_width: i32,
198        display_height: i32,
199        inspect_node: &fuchsia_inspect::Node,
200    ) -> Self {
201        let node = inspect_node.create_child("touch_device");
202        InputDevice {
203            device_type: InputDeviceType::Touch(display_width, display_height),
204            open_files: Default::default(),
205            inspect_status: Arc::new(InputDeviceStatus::new(node)),
206        }
207    }
208
209    pub fn new_keyboard(inspect_node: &fuchsia_inspect::Node) -> Self {
210        let node = inspect_node.create_child("keyboard_device");
211        InputDevice {
212            device_type: InputDeviceType::Keyboard,
213            open_files: Default::default(),
214            inspect_status: Arc::new(InputDeviceStatus::new(node)),
215        }
216    }
217
218    pub fn new_mouse(inspect_node: &fuchsia_inspect::Node) -> Self {
219        let node = inspect_node.create_child("mouse_device");
220        InputDevice {
221            device_type: InputDeviceType::Mouse,
222            open_files: Default::default(),
223            inspect_status: Arc::new(InputDeviceStatus::new(node)),
224        }
225    }
226
227    pub fn register<L>(
228        self,
229        locked: &mut Locked<L>,
230        system_task: &CurrentTask,
231        device_id: DeviceId,
232    ) -> Result<(), Errno>
233    where
234        L: LockEqualOrBefore<FileOpsCore>,
235    {
236        let kernel = system_task.kernel();
237        let registry = &kernel.device_registry;
238
239        let input_class = registry.objects.input_class();
240        registry.register_device(
241            locked,
242            system_task,
243            FsString::from(format!("event{}", device_id)).as_ref(),
244            DeviceMetadata::new(
245                format!("input/event{}", device_id).into(),
246                DeviceType::new(INPUT_MAJOR, device_id),
247                DeviceMode::Char,
248            ),
249            input_class,
250            self,
251        )?;
252        Ok(())
253    }
254
255    pub fn open_internal(&self) -> Box<dyn FileOps> {
256        let input_file = match self.device_type {
257            InputDeviceType::Touch(display_width, display_height) => {
258                let mut file_nodes = self.inspect_status.file_nodes.lock();
259                let child_node = self
260                    .inspect_status
261                    .node
262                    .create_child(format!("touch_file_{}", file_nodes.len()));
263                let file = Arc::new(InputFile::new_touch(
264                    TOUCH_INPUT_ID,
265                    display_width,
266                    display_height,
267                    Some(&child_node),
268                ));
269                file_nodes.push(child_node);
270                file
271            }
272            InputDeviceType::Keyboard => {
273                let mut file_nodes = self.inspect_status.file_nodes.lock();
274                let child_node = self
275                    .inspect_status
276                    .node
277                    .create_child(format!("keyboard_file_{}", file_nodes.len()));
278                let file = Arc::new(InputFile::new_keyboard(KEYBOARD_INPUT_ID, Some(&child_node)));
279                file_nodes.push(child_node);
280                file
281            }
282            InputDeviceType::Mouse => {
283                let mut file_nodes = self.inspect_status.file_nodes.lock();
284                let child_node = self
285                    .inspect_status
286                    .node
287                    .create_child(format!("mouse_file_{}", file_nodes.len()));
288                let file = Arc::new(InputFile::new_mouse(MOUSE_INPUT_ID, Some(&child_node)));
289                file_nodes.push(child_node);
290                file
291            }
292        };
293        self.open_files.lock().push(Arc::downgrade(&input_file));
294        Box::new(input_file)
295    }
296
297    #[cfg(test)]
298    pub fn open_test(
299        &self,
300        locked: &mut Locked<Unlocked>,
301        current_task: &CurrentTask,
302    ) -> Result<starnix_core::vfs::FileHandle, Errno> {
303        let input_file = self.open_internal();
304        let root_namespace_node = current_task
305            .lookup_path_from_root(locked, ".".into())
306            .expect("failed to get namespace node for root");
307
308        let file_object = starnix_core::vfs::FileObject::new(
309            locked,
310            current_task,
311            input_file,
312            root_namespace_node,
313            OpenFlags::empty(),
314        )
315        .expect("FileObject::new failed");
316        Ok(file_object)
317    }
318}
319
320impl DeviceOps for InputDevice {
321    fn open(
322        &self,
323        _locked: &mut Locked<FileOpsCore>,
324        _current_task: &CurrentTask,
325        _id: DeviceType,
326        _node: &NamespaceNode,
327        _flags: OpenFlags,
328    ) -> Result<Box<dyn FileOps>, Errno> {
329        let input_file = self.open_internal();
330        Ok(input_file)
331    }
332}
333
334#[cfg(test)]
335mod test {
336    #![allow(clippy::unused_unit)] // for compatibility with `test_case`
337
338    use super::*;
339    use crate::input_event_relay::{self, EventProxyMode};
340    use anyhow::anyhow;
341    use assert_matches::assert_matches;
342    use diagnostics_assertions::{AnyProperty, assert_data_tree};
343    use fidl_fuchsia_ui_input::MediaButtonsEvent;
344    use fuipointer::{
345        EventPhase, TouchEvent, TouchInteractionId, TouchPointerSample, TouchResponse,
346        TouchSourceMarker, TouchSourceRequest,
347    };
348    use futures::StreamExt as _;
349    use pretty_assertions::assert_eq;
350    use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
351    use starnix_core::task::{EventHandler, Waiter};
352    #[allow(deprecated, reason = "pre-existing usage")]
353    use starnix_core::testing::create_kernel_task_and_unlocked;
354    use starnix_core::vfs::FileHandle;
355    use starnix_core::vfs::buffers::VecOutputBuffer;
356    use starnix_types::time::timeval_from_time;
357    use starnix_uapi::errors::EAGAIN;
358    use starnix_uapi::uapi;
359    use starnix_uapi::vfs::FdEvents;
360    use test_case::test_case;
361    use test_util::assert_near;
362    use zerocopy::FromBytes as _;
363    use {
364        fidl_fuchsia_ui_input3 as fuiinput, fidl_fuchsia_ui_pointer as fuipointer,
365        fidl_fuchsia_ui_policy as fuipolicy,
366    };
367
368    const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
369
370    async fn start_touch_input(
371        locked: &mut Locked<Unlocked>,
372        current_task: &CurrentTask,
373    ) -> (InputDevice, FileHandle, fuipointer::TouchSourceRequestStream) {
374        let inspector = fuchsia_inspect::Inspector::default();
375        start_touch_input_inspect_and_dimensions(locked, current_task, 700, 1200, &inspector).await
376    }
377
378    async fn start_touch_input_inspect(
379        locked: &mut Locked<Unlocked>,
380        current_task: &CurrentTask,
381        inspector: &fuchsia_inspect::Inspector,
382    ) -> (InputDevice, FileHandle, fuipointer::TouchSourceRequestStream) {
383        start_touch_input_inspect_and_dimensions(locked, current_task, 700, 1200, &inspector).await
384    }
385
386    async fn init_keyboard_listener(
387        keyboard_stream: &mut fuiinput::KeyboardRequestStream,
388    ) -> fuiinput::KeyboardListenerProxy {
389        let keyboard_listener = match keyboard_stream.next().await {
390            Some(Ok(fuiinput::KeyboardRequest::AddListener {
391                view_ref: _,
392                listener,
393                responder,
394            })) => {
395                let _ = responder.send();
396                listener.into_proxy()
397            }
398            _ => {
399                panic!("Failed to get event");
400            }
401        };
402
403        keyboard_listener
404    }
405
406    async fn init_button_listeners(
407        device_listener_stream: &mut fuipolicy::DeviceListenerRegistryRequestStream,
408    ) -> (fuipolicy::MediaButtonsListenerProxy, fuipolicy::TouchButtonsListenerProxy) {
409        let media_buttons_listener = match device_listener_stream.next().await {
410            Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
411                listener,
412                responder,
413            })) => {
414                let _ = responder.send();
415                listener.into_proxy()
416            }
417            _ => {
418                panic!("Failed to get event");
419            }
420        };
421
422        let touch_buttons_listener = match device_listener_stream.next().await {
423            Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
424                listener,
425                responder,
426            })) => {
427                let _ = responder.send();
428                listener.into_proxy()
429            }
430            _ => {
431                panic!("Failed to get event");
432            }
433        };
434
435        (media_buttons_listener, touch_buttons_listener)
436    }
437
438    async fn start_touch_input_inspect_and_dimensions(
439        locked: &mut Locked<Unlocked>,
440        current_task: &CurrentTask,
441        x_max: i32,
442        y_max: i32,
443        inspector: &fuchsia_inspect::Inspector,
444    ) -> (InputDevice, FileHandle, fuipointer::TouchSourceRequestStream) {
445        let input_device = InputDevice::new_touch(x_max, y_max, inspector.root());
446        let input_file =
447            input_device.open_test(locked, current_task).expect("Failed to create input file");
448
449        let (touch_source_client_end, touch_source_stream) =
450            fidl::endpoints::create_request_stream::<TouchSourceMarker>();
451
452        let (mouse_source_client_end, _mouse_source_stream) =
453            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
454
455        let (keyboard_proxy, mut keyboard_stream) =
456            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
457        let view_ref_pair =
458            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
459
460        let (device_registry_proxy, mut device_listener_stream) =
461            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
462            );
463
464        let (relay, _relay_handle) = input_event_relay::new_input_relay();
465        relay.start_relays(
466            &current_task.kernel(),
467            EventProxyMode::None,
468            touch_source_client_end,
469            keyboard_proxy,
470            mouse_source_client_end,
471            view_ref_pair.view_ref,
472            device_registry_proxy,
473            input_device.open_files.clone(),
474            Default::default(),
475            Default::default(),
476            Some(input_device.inspect_status.clone()),
477            None,
478            None,
479        );
480
481        let _ = init_keyboard_listener(&mut keyboard_stream).await;
482        let _ = init_button_listeners(&mut device_listener_stream).await;
483
484        (input_device, input_file, touch_source_stream)
485    }
486
487    async fn start_keyboard_input(
488        locked: &mut Locked<Unlocked>,
489        current_task: &CurrentTask,
490    ) -> (InputDevice, FileHandle, fuiinput::KeyboardListenerProxy) {
491        let inspector = fuchsia_inspect::Inspector::default();
492        let input_device = InputDevice::new_keyboard(inspector.root());
493        let input_file =
494            input_device.open_test(locked, current_task).expect("Failed to create input file");
495        let (keyboard_proxy, mut keyboard_stream) =
496            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
497        let view_ref_pair =
498            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
499
500        let (device_registry_proxy, mut device_listener_stream) =
501            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
502            );
503
504        let (touch_source_client_end, _touch_source_stream) =
505            fidl::endpoints::create_request_stream::<TouchSourceMarker>();
506
507        let (mouse_source_client_end, _mouse_source_stream) =
508            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
509
510        let (relay, _relay_handle) = input_event_relay::new_input_relay();
511        relay.start_relays(
512            current_task.kernel(),
513            EventProxyMode::None,
514            touch_source_client_end,
515            keyboard_proxy,
516            mouse_source_client_end,
517            view_ref_pair.view_ref,
518            device_registry_proxy,
519            Default::default(),
520            input_device.open_files.clone(),
521            Default::default(),
522            None,
523            Some(input_device.inspect_status.clone()),
524            None,
525        );
526
527        let keyboad_listener = init_keyboard_listener(&mut keyboard_stream).await;
528        let _ = init_button_listeners(&mut device_listener_stream).await;
529
530        (input_device, input_file, keyboad_listener)
531    }
532
533    async fn start_button_input(
534        locked: &mut Locked<Unlocked>,
535        current_task: &CurrentTask,
536    ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
537        let inspector = fuchsia_inspect::Inspector::default();
538        start_button_input_inspect(locked, current_task, &inspector).await
539    }
540
541    async fn start_button_input_inspect(
542        locked: &mut Locked<Unlocked>,
543        current_task: &CurrentTask,
544        inspector: &fuchsia_inspect::Inspector,
545    ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
546        let input_device = InputDevice::new_keyboard(inspector.root());
547        let input_file =
548            input_device.open_test(locked, current_task).expect("Failed to create input file");
549        let (device_registry_proxy, mut device_listener_stream) =
550            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
551            );
552
553        let (touch_source_client_end, _touch_source_stream) =
554            fidl::endpoints::create_request_stream::<TouchSourceMarker>();
555        let (mouse_source_client_end, _mouse_source_stream) =
556            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
557        let (keyboard_proxy, mut keyboard_stream) =
558            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
559        let view_ref_pair =
560            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
561
562        let (relay, _relay_handle) = input_event_relay::new_input_relay();
563        relay.start_relays(
564            current_task.kernel(),
565            EventProxyMode::None,
566            touch_source_client_end,
567            keyboard_proxy,
568            mouse_source_client_end,
569            view_ref_pair.view_ref,
570            device_registry_proxy,
571            Default::default(),
572            input_device.open_files.clone(),
573            Default::default(),
574            None,
575            Some(input_device.inspect_status.clone()),
576            None,
577        );
578
579        let _ = init_keyboard_listener(&mut keyboard_stream).await;
580        let (button_listener, _) = init_button_listeners(&mut device_listener_stream).await;
581
582        (input_device, input_file, button_listener)
583    }
584
585    async fn start_mouse_input(
586        locked: &mut Locked<Unlocked>,
587        current_task: &CurrentTask,
588    ) -> (InputDevice, FileHandle, fuipointer::MouseSourceRequestStream) {
589        let inspector = fuchsia_inspect::Inspector::default();
590        start_mouse_input_inspect(locked, current_task, &inspector).await
591    }
592
593    async fn start_mouse_input_inspect(
594        locked: &mut Locked<Unlocked>,
595        current_task: &CurrentTask,
596        inspector: &fuchsia_inspect::Inspector,
597    ) -> (InputDevice, FileHandle, fuipointer::MouseSourceRequestStream) {
598        let input_device = InputDevice::new_mouse(inspector.root());
599        let input_file =
600            input_device.open_test(locked, current_task).expect("Failed to create input file");
601
602        let (touch_source_client_end, _touch_source_stream) =
603            fidl::endpoints::create_request_stream::<TouchSourceMarker>();
604
605        let (mouse_source_client_end, mouse_source_stream) =
606            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
607
608        let (keyboard_proxy, mut keyboard_stream) =
609            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
610        let view_ref_pair =
611            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
612
613        let (device_registry_proxy, mut device_listener_stream) =
614            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
615            );
616
617        let (relay, _relay_handle) = input_event_relay::new_input_relay();
618        relay.start_relays(
619            &current_task.kernel(),
620            EventProxyMode::None,
621            touch_source_client_end,
622            keyboard_proxy,
623            mouse_source_client_end,
624            view_ref_pair.view_ref,
625            device_registry_proxy,
626            Default::default(),
627            Default::default(),
628            input_device.open_files.clone(),
629            None,
630            None,
631            Some(input_device.inspect_status.clone()),
632        );
633
634        let _ = init_keyboard_listener(&mut keyboard_stream).await;
635        let _ = init_button_listeners(&mut device_listener_stream).await;
636
637        (input_device, input_file, mouse_source_stream)
638    }
639
640    fn make_touch_event(pointer_id: u32) -> fuipointer::TouchEvent {
641        // Default to `Change`, because that has the fewest side effects.
642        make_touch_event_with_phase(EventPhase::Change, pointer_id)
643    }
644
645    fn make_touch_event_with_phase(phase: EventPhase, pointer_id: u32) -> fuipointer::TouchEvent {
646        make_touch_event_with_coords_phase(0.0, 0.0, phase, pointer_id)
647    }
648
649    fn make_touch_event_with_coords_phase(
650        x: f32,
651        y: f32,
652        phase: EventPhase,
653        pointer_id: u32,
654    ) -> fuipointer::TouchEvent {
655        make_touch_event_with_coords_phase_timestamp(x, y, phase, pointer_id, 0)
656    }
657
658    fn make_touch_event_with_coords(x: f32, y: f32, pointer_id: u32) -> fuipointer::TouchEvent {
659        make_touch_event_with_coords_phase(x, y, EventPhase::Change, pointer_id)
660    }
661
662    fn make_touch_event_with_coords_phase_timestamp(
663        x: f32,
664        y: f32,
665        phase: EventPhase,
666        pointer_id: u32,
667        time_nanos: i64,
668    ) -> fuipointer::TouchEvent {
669        make_touch_event_with_coords_phase_timestamp_device_id(
670            x, y, phase, pointer_id, time_nanos, 0,
671        )
672    }
673
674    fn make_empty_touch_event() -> fuipointer::TouchEvent {
675        TouchEvent {
676            pointer_sample: Some(TouchPointerSample {
677                interaction: Some(TouchInteractionId {
678                    pointer_id: 0,
679                    device_id: 0,
680                    interaction_id: 0,
681                }),
682                ..Default::default()
683            }),
684            ..Default::default()
685        }
686    }
687
688    fn make_touch_event_with_coords_phase_timestamp_device_id(
689        x: f32,
690        y: f32,
691        phase: EventPhase,
692        pointer_id: u32,
693        time_nanos: i64,
694        device_id: u32,
695    ) -> fuipointer::TouchEvent {
696        TouchEvent {
697            timestamp: Some(time_nanos),
698            pointer_sample: Some(TouchPointerSample {
699                position_in_viewport: Some([x, y]),
700                // Default to `Change`, because that has the fewest side effects.
701                phase: Some(phase),
702                interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
703                ..Default::default()
704            }),
705            ..Default::default()
706        }
707    }
708
709    fn make_mouse_wheel_event(ticks: i64) -> fuipointer::MouseEvent {
710        make_mouse_wheel_event_with_timestamp(ticks, 0)
711    }
712
713    fn make_mouse_wheel_event_with_timestamp(ticks: i64, timestamp: i64) -> fuipointer::MouseEvent {
714        fuipointer::MouseEvent {
715            timestamp: Some(timestamp),
716            pointer_sample: Some(fuipointer::MousePointerSample {
717                device_id: Some(0),
718                scroll_v: Some(ticks),
719                ..Default::default()
720            }),
721            ..Default::default()
722        }
723    }
724
725    fn read_uapi_events<L>(
726        locked: &mut Locked<L>,
727        file: &FileHandle,
728        current_task: &CurrentTask,
729    ) -> Vec<uapi::input_event>
730    where
731        L: LockEqualOrBefore<FileOpsCore>,
732    {
733        std::iter::from_fn(|| {
734            let locked = locked.cast_locked::<FileOpsCore>();
735            let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
736            match file.read(locked, current_task, &mut event_bytes) {
737                Ok(INPUT_EVENT_SIZE) => Some(
738                    uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
739                        .map_err(|_| anyhow!("failed to read input_event from buffer")),
740                ),
741                Ok(other_size) => {
742                    Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
743                }
744                Err(Errno { code: EAGAIN, .. }) => None,
745                Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
746            }
747        })
748        .enumerate()
749        .map(|(i, read_res)| match read_res {
750            Ok(event) => event,
751            Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
752        })
753        .collect()
754    }
755
756    // Waits for a `Watch()` request to arrive on `request_stream`, and responds with
757    // `touch_event`. Returns the arguments to the `Watch()` call.
758    async fn answer_next_touch_watch_request(
759        request_stream: &mut fuipointer::TouchSourceRequestStream,
760        touch_events: Vec<TouchEvent>,
761    ) -> Vec<TouchResponse> {
762        match request_stream.next().await {
763            Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
764                responder.send(touch_events).expect("failure sending Watch reply");
765                responses
766            }
767            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
768        }
769    }
770
771    // Waits for a `Watch()` request to arrive on `request_stream`, and responds with
772    // `mouse_events`.
773    async fn answer_next_mouse_watch_request(
774        request_stream: &mut fuipointer::MouseSourceRequestStream,
775        mouse_events: Vec<fuipointer::MouseEvent>,
776    ) {
777        match request_stream.next().await {
778            Some(Ok(fuipointer::MouseSourceRequest::Watch { responder })) => {
779                responder.send(mouse_events).expect("failure sending Watch reply");
780            }
781            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
782        }
783    }
784
785    #[::fuchsia::test()]
786    async fn initial_watch_request_has_empty_responses_arg() {
787        #[allow(deprecated, reason = "pre-existing usage")]
788        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
789        // Set up resources.
790        let (_input_device, _input_file, mut touch_source_stream) =
791            start_touch_input(locked, &current_task).await;
792
793        // Verify that the watch request has empty `responses`.
794        assert_matches!(
795            touch_source_stream.next().await,
796            Some(Ok(TouchSourceRequest::Watch { responses, .. }))
797                => assert_eq!(responses.as_slice(), [])
798        );
799    }
800
801    #[::fuchsia::test]
802    async fn later_watch_requests_have_responses_arg_matching_earlier_watch_replies() {
803        // Set up resources.
804        #[allow(deprecated, reason = "pre-existing usage")]
805        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
806        let (_input_device, _input_file, mut touch_source_stream) =
807            start_touch_input(locked, &current_task).await;
808
809        // Reply to first `Watch` with two `TouchEvent`s.
810        match touch_source_stream.next().await {
811            Some(Ok(TouchSourceRequest::Watch { responder, .. })) => responder
812                .send(vec![make_empty_touch_event(), make_empty_touch_event()])
813                .expect("failure sending Watch reply"),
814            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
815        }
816
817        // Verify second `Watch` has two elements in `responses`.
818        // Then reply with five `TouchEvent`s.
819        match touch_source_stream.next().await {
820            Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
821                assert_matches!(responses.as_slice(), [_, _]);
822                responder
823                    .send(vec![
824                        make_empty_touch_event(),
825                        make_empty_touch_event(),
826                        make_empty_touch_event(),
827                        make_empty_touch_event(),
828                        make_empty_touch_event(),
829                    ])
830                    .expect("failure sending Watch reply")
831            }
832            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
833        }
834
835        // Verify third `Watch` has five elements in `responses`.
836        match touch_source_stream.next().await {
837            Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
838                assert_matches!(responses.as_slice(), [_, _, _, _, _]);
839            }
840            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
841        }
842    }
843
844    #[::fuchsia::test]
845    async fn notifies_polling_waiters_of_new_data() {
846        // Set up resources.
847        #[allow(deprecated, reason = "pre-existing usage")]
848        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
849        let (_input_device, input_file, mut touch_source_stream) =
850            start_touch_input(locked, &current_task).await;
851        let waiter1 = Waiter::new();
852        let waiter2 = Waiter::new();
853
854        // Ask `input_file` to notify waiters when data is available to read.
855        [&waiter1, &waiter2].iter().for_each(|waiter| {
856            input_file.wait_async(
857                locked,
858                &current_task,
859                waiter,
860                FdEvents::POLLIN,
861                EventHandler::None,
862            );
863        });
864        assert_matches!(
865            waiter1.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
866            Err(_)
867        );
868        assert_matches!(
869            waiter2.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
870            Err(_)
871        );
872
873        // Reply to first `Watch` request.
874        answer_next_touch_watch_request(
875            &mut touch_source_stream,
876            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
877        )
878        .await;
879        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
880
881        // `InputFile` should be done processing the first reply, since it has sent its second
882        // request. And, as part of processing the first reply, `InputFile` should have notified
883        // the interested waiters.
884        assert_eq!(waiter1.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO), Ok(()));
885        assert_eq!(waiter2.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO), Ok(()));
886    }
887
888    #[::fuchsia::test]
889    async fn notifies_blocked_waiter_of_new_data() {
890        // Set up resources.
891        #[allow(deprecated, reason = "pre-existing usage")]
892        let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
893        let (_input_device, input_file, mut touch_source_stream) =
894            start_touch_input(locked, &current_task).await;
895        let waiter = Waiter::new();
896
897        // Ask `input_file` to notify `waiter` when data is available to read.
898        input_file.wait_async(locked, &current_task, &waiter, FdEvents::POLLIN, EventHandler::None);
899
900        let closure =
901            move |locked: &mut Locked<Unlocked>, task: &CurrentTask| waiter.wait(locked, &task);
902
903        let (waiter_thread, req) = SpawnRequestBuilder::new()
904            .with_debug_name("input-device-waiter")
905            .with_sync_closure(closure)
906            .build_with_async_result();
907        kernel.kthreads.spawner().spawn_from_request(req);
908
909        let mut waiter_thread = Box::pin(waiter_thread);
910        assert_matches!(futures::poll!(&mut waiter_thread), futures::task::Poll::Pending);
911
912        // Reply to first `Watch` request.
913        answer_next_touch_watch_request(
914            &mut touch_source_stream,
915            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
916        )
917        .await;
918
919        // Wait for another `Watch`.
920        //
921        // TODO(https://fxbug.dev/42075452): Without this, `relay_thread` gets stuck `await`-ing
922        // the reply to its first request. Figure out why that happens, and remove this second
923        // reply.
924        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
925    }
926
927    #[::fuchsia::test]
928    async fn does_not_notify_polling_waiters_without_new_data() {
929        // Set up resources.
930        #[allow(deprecated, reason = "pre-existing usage")]
931        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
932        let (_input_device, input_file, mut touch_source_stream) =
933            start_touch_input(locked, &current_task).await;
934        let waiter1 = Waiter::new();
935        let waiter2 = Waiter::new();
936
937        // Ask `input_file` to notify waiters when data is available to read.
938        [&waiter1, &waiter2].iter().for_each(|waiter| {
939            input_file.wait_async(
940                locked,
941                &current_task,
942                waiter,
943                FdEvents::POLLIN,
944                EventHandler::None,
945            );
946        });
947        assert_matches!(
948            waiter1.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
949            Err(_)
950        );
951        assert_matches!(
952            waiter2.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
953            Err(_)
954        );
955
956        // Reply to first `Watch` request with an empty set of events.
957        answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
958
959        // `InputFile` should be done processing the first reply. Since there
960        // were no touch_events given, `InputFile` should not have notified the
961        // interested waiters.
962        assert_matches!(
963            waiter1.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
964            Err(_)
965        );
966        assert_matches!(
967            waiter2.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
968            Err(_)
969        );
970    }
971
972    // Note: a user program may also want to be woken if events were already ready at the
973    // time that the program called `epoll_wait()`. However, there's no test for that case
974    // in this module, because:
975    //
976    // 1. Not all programs will want to be woken in such a case. In particular, some programs
977    //    use "edge-triggered" mode instead of "level-tiggered" mode. For details on the
978    //    two modes, see https://man7.org/linux/man-pages/man7/epoll.7.html.
979    // 2. For programs using "level-triggered" mode, the relevant behavior is implemented in
980    //    the `epoll` module, and verified by `epoll::tests::test_epoll_ready_then_wait()`.
981    //
982    // See also: the documentation for `FileOps::wait_async()`.
983
984    #[::fuchsia::test]
985    async fn honors_wait_cancellation() {
986        // Set up input resources.
987        #[allow(deprecated, reason = "pre-existing usage")]
988        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
989        let (_input_device, input_file, mut touch_source_stream) =
990            start_touch_input(locked, &current_task).await;
991        let waiter1 = Waiter::new();
992        let waiter2 = Waiter::new();
993
994        // Ask `input_file` to notify `waiter` when data is available to read.
995        let waitkeys = [&waiter1, &waiter2]
996            .iter()
997            .map(|waiter| {
998                input_file
999                    .wait_async(locked, &current_task, waiter, FdEvents::POLLIN, EventHandler::None)
1000                    .expect("wait_async")
1001            })
1002            .collect::<Vec<_>>();
1003
1004        // Cancel wait for `waiter1`.
1005        waitkeys.into_iter().next().expect("failed to get first waitkey").cancel();
1006
1007        // Reply to first `Watch` request.
1008        answer_next_touch_watch_request(
1009            &mut touch_source_stream,
1010            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1011        )
1012        .await;
1013        // Wait for another `Watch`.
1014        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
1015
1016        // `InputFile` should be done processing the first reply, since it has sent its second
1017        // request. And, as part of processing the first reply, `InputFile` should have notified
1018        // the interested waiters.
1019        assert_matches!(
1020            waiter1.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO),
1021            Err(_)
1022        );
1023        assert_eq!(waiter2.wait_until(locked, &current_task, zx::MonotonicInstant::ZERO), Ok(()));
1024    }
1025
1026    #[::fuchsia::test]
1027    async fn query_events() {
1028        // Set up resources.
1029        #[allow(deprecated, reason = "pre-existing usage")]
1030        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1031        let (_input_device, input_file, mut touch_source_stream) =
1032            start_touch_input(locked, &current_task).await;
1033
1034        // Check initial expectation.
1035        assert_eq!(
1036            input_file.query_events(locked, &current_task).expect("query_events"),
1037            FdEvents::empty(),
1038            "events should be empty before data arrives"
1039        );
1040
1041        // Reply to first `Watch` request.
1042        answer_next_touch_watch_request(
1043            &mut touch_source_stream,
1044            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1045        )
1046        .await;
1047
1048        // Wait for another `Watch`.
1049        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)]).await;
1050
1051        // Check post-watch expectation.
1052        assert_eq!(
1053            input_file.query_events(locked, &current_task).expect("query_events"),
1054            FdEvents::POLLIN | FdEvents::POLLRDNORM,
1055            "events should be POLLIN after data arrives"
1056        );
1057    }
1058
1059    fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1060        make_uapi_input_event_with_timestamp(ty, code, value, 0)
1061    }
1062
1063    fn make_uapi_input_event_with_timestamp(
1064        ty: u32,
1065        code: u32,
1066        value: i32,
1067        time_nanos: i64,
1068    ) -> uapi::input_event {
1069        uapi::input_event {
1070            time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
1071            type_: ty as u16,
1072            code: code as u16,
1073            value,
1074        }
1075    }
1076
1077    #[::fuchsia::test]
1078    async fn touch_event_ignored() {
1079        // Set up resources.
1080        let inspector = fuchsia_inspect::Inspector::default();
1081        #[allow(deprecated, reason = "pre-existing usage")]
1082        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1083        let (_input_device, input_file, mut touch_source_stream) =
1084            start_touch_input_inspect(locked, &current_task, &inspector).await;
1085
1086        // Touch add for pointer 1. This should be counted as a received event and a converted
1087        // event. It should also yield 6 generated events.
1088        answer_next_touch_watch_request(
1089            &mut touch_source_stream,
1090            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1091        )
1092        .await;
1093
1094        // Wait for another `Watch` to ensure input_file done processing the first reply.
1095        // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1096        // `uapi::input_event`s. This should be counted as a received event and an ignored event.
1097        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1098            .await;
1099
1100        // Consume all of the `uapi::input_event`s that are available.
1101        let events = read_uapi_events(locked, &input_file, &current_task);
1102
1103        assert_eq!(events.len(), 6);
1104
1105        // Reply to `Watch` request of empty event. This should be counted as a received event and
1106        // an ignored event.
1107        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1108            .await;
1109
1110        // Wait for another `Watch`.
1111        match touch_source_stream.next().await {
1112            Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
1113                assert_matches!(responses.as_slice(), [_])
1114            }
1115            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1116        }
1117
1118        let events = read_uapi_events(locked, &input_file, &current_task);
1119        assert_eq!(events, vec![]);
1120        assert_data_tree!(inspector, root: {
1121            touch_device: {
1122                total_fidl_events_received_count: 3u64,
1123                total_fidl_events_ignored_count: 2u64,
1124                total_fidl_events_unexpected_count: 0u64,
1125                total_fidl_events_converted_count: 1u64,
1126                total_uapi_events_generated_count: 6u64,
1127                last_generated_uapi_event_timestamp_ns: 0i64,
1128                touch_file_0: {
1129                    fidl_events_received_count: 3u64,
1130                    fidl_events_ignored_count: 2u64,
1131                    fidl_events_unexpected_count: 0u64,
1132                    fidl_events_converted_count: 1u64,
1133                    uapi_events_generated_count: 6u64,
1134                    uapi_events_read_count: 6u64,
1135                    last_generated_uapi_event_timestamp_ns: 0i64,
1136                    last_read_uapi_event_timestamp_ns: 0i64,
1137                },
1138            }
1139        });
1140    }
1141
1142    #[test_case(make_touch_event_with_phase(EventPhase::Add, 1); "touch add for pointer already added")]
1143    #[test_case(make_touch_event_with_phase(EventPhase::Change, 2); "touch change for pointer not added")]
1144    #[test_case(make_touch_event_with_phase(EventPhase::Remove, 2); "touch remove for pointer not added")]
1145    #[test_case(make_touch_event_with_phase(EventPhase::Cancel, 1); "touch cancel")]
1146    #[::fuchsia::test]
1147    async fn touch_event_unexpected(event: TouchEvent) {
1148        // Set up resources.
1149        let inspector = fuchsia_inspect::Inspector::default();
1150        #[allow(deprecated, reason = "pre-existing usage")]
1151        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1152        let (_input_device, input_file, mut touch_source_stream) =
1153            start_touch_input_inspect(locked, &current_task, &inspector).await;
1154
1155        // Touch add for pointer 1. This should be counted as a received event and a converted
1156        // event. It should also yield 6 generated events.
1157        answer_next_touch_watch_request(
1158            &mut touch_source_stream,
1159            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1160        )
1161        .await;
1162
1163        // Wait for another `Watch` to ensure input_file done processing the first reply.
1164        // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1165        // `uapi::input_event`s. This should be counted as a received event and an ignored event.
1166        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1167            .await;
1168
1169        // Consume all of the `uapi::input_event`s that are available.
1170        let events = read_uapi_events(locked, &input_file, &current_task);
1171
1172        assert_eq!(events.len(), 6);
1173
1174        // Reply to `Watch` request of given event. This should be counted as a received event and
1175        // an unexpected event.
1176        answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1177
1178        // Wait for another `Watch`.
1179        match touch_source_stream.next().await {
1180            Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
1181                assert_matches!(responses.as_slice(), [_])
1182            }
1183            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1184        }
1185
1186        let events = read_uapi_events(locked, &input_file, &current_task);
1187        assert_eq!(events, vec![]);
1188        assert_data_tree!(inspector, root: {
1189            touch_device: {
1190                total_fidl_events_received_count: 3u64,
1191                total_fidl_events_ignored_count: 1u64,
1192                total_fidl_events_unexpected_count: 1u64,
1193                total_fidl_events_converted_count: 1u64,
1194                total_uapi_events_generated_count: 6u64,
1195                last_generated_uapi_event_timestamp_ns: 0i64,
1196                touch_file_0: {
1197                    fidl_events_received_count: 3u64,
1198                    fidl_events_ignored_count: 1u64,
1199                    fidl_events_unexpected_count: 1u64,
1200                    fidl_events_converted_count: 1u64,
1201                    uapi_events_generated_count: 6u64,
1202                    uapi_events_read_count: 6u64,
1203                    last_generated_uapi_event_timestamp_ns: 0i64,
1204                    last_read_uapi_event_timestamp_ns: 0i64,
1205                },
1206            }
1207        });
1208    }
1209
1210    #[::fuchsia::test]
1211    async fn translates_touch_add() {
1212        // Set up resources.
1213        #[allow(deprecated, reason = "pre-existing usage")]
1214        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1215        let (_input_device, input_file, mut touch_source_stream) =
1216            start_touch_input(locked, &current_task).await;
1217
1218        // Touch add for pointer 1.
1219        answer_next_touch_watch_request(
1220            &mut touch_source_stream,
1221            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1222        )
1223        .await;
1224
1225        // Wait for another `Watch` to ensure input_file done processing the first reply.
1226        // Use an empty `TouchEvent`, to minimize the chance that this event
1227        // creates unexpected `uapi::input_event`s.
1228        answer_next_touch_watch_request(&mut touch_source_stream, vec![make_empty_touch_event()])
1229            .await;
1230
1231        // Consume all of the `uapi::input_event`s that are available.
1232        let events = read_uapi_events(locked, &input_file, &current_task);
1233
1234        assert_eq!(
1235            events,
1236            vec![
1237                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1238                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1239                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1240                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1241                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1242                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1243            ]
1244        );
1245    }
1246
1247    #[::fuchsia::test]
1248    async fn translates_touch_change() {
1249        // Set up resources.
1250        #[allow(deprecated, reason = "pre-existing usage")]
1251        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1252        let (_input_device, input_file, mut touch_source_stream) =
1253            start_touch_input(locked, &current_task).await;
1254
1255        // Touch add for pointer 1.
1256        answer_next_touch_watch_request(
1257            &mut touch_source_stream,
1258            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1259        )
1260        .await;
1261
1262        // Wait for another `Watch` to ensure input_file done processing the first reply.
1263        // Use an empty `TouchEvent`, to minimize the chance that this event
1264        // creates unexpected `uapi::input_event`s.
1265        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1266            .await;
1267
1268        // Consume all of the `uapi::input_event`s that are available.
1269        let events = read_uapi_events(locked, &input_file, &current_task);
1270
1271        assert_eq!(events.len(), 6);
1272
1273        // Reply to touch change.
1274        answer_next_touch_watch_request(
1275            &mut touch_source_stream,
1276            vec![make_touch_event_with_coords(10.0, 20.0, 1)],
1277        )
1278        .await;
1279
1280        // Wait for another `Watch`.
1281        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1282            .await;
1283
1284        let events = read_uapi_events(locked, &input_file, &current_task);
1285        assert_eq!(
1286            events,
1287            vec![
1288                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1289                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1290                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1291                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1292            ]
1293        );
1294    }
1295
1296    #[::fuchsia::test]
1297    async fn translates_touch_remove() {
1298        // Set up resources.
1299        #[allow(deprecated, reason = "pre-existing usage")]
1300        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1301        let (_input_device, input_file, mut touch_source_stream) =
1302            start_touch_input(locked, &current_task).await;
1303
1304        // Touch add for pointer 1.
1305        answer_next_touch_watch_request(
1306            &mut touch_source_stream,
1307            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1308        )
1309        .await;
1310
1311        // Wait for another `Watch` to ensure input_file done processing the first reply.
1312        // Use an empty `TouchEvent`, to minimize the chance that this event
1313        // creates unexpected `uapi::input_event`s.
1314        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1315            .await;
1316
1317        // Consume all of the `uapi::input_event`s that are available.
1318        let events = read_uapi_events(locked, &input_file, &current_task);
1319
1320        assert_eq!(events.len(), 6);
1321
1322        // Reply to touch change.
1323        answer_next_touch_watch_request(
1324            &mut touch_source_stream,
1325            vec![make_touch_event_with_phase(EventPhase::Remove, 1)],
1326        )
1327        .await;
1328
1329        // Wait for another `Watch`.
1330        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1331            .await;
1332
1333        let events = read_uapi_events(locked, &input_file, &current_task);
1334        assert_eq!(
1335            events,
1336            vec![
1337                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1338                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1339                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1340                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1341            ]
1342        );
1343    }
1344
1345    #[::fuchsia::test]
1346    async fn multi_touch_event_sequence() {
1347        // Set up resources.
1348        #[allow(deprecated, reason = "pre-existing usage")]
1349        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1350        let (_input_device, input_file, mut touch_source_stream) =
1351            start_touch_input(locked, &current_task).await;
1352
1353        // Touch add for pointer 1.
1354        answer_next_touch_watch_request(
1355            &mut touch_source_stream,
1356            vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1357        )
1358        .await;
1359
1360        // Wait for another `Watch`.
1361        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1362            .await;
1363        let events = read_uapi_events(locked, &input_file, &current_task);
1364
1365        assert_eq!(events.len(), 6);
1366
1367        // Touch add for pointer 2.
1368        answer_next_touch_watch_request(
1369            &mut touch_source_stream,
1370            vec![
1371                make_touch_event_with_coords(10.0, 20.0, 1),
1372                make_touch_event_with_phase(EventPhase::Add, 2),
1373            ],
1374        )
1375        .await;
1376
1377        // Wait for another `Watch`.
1378        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1379            .await;
1380        let events = read_uapi_events(locked, &input_file, &current_task);
1381
1382        assert_eq!(
1383            events,
1384            vec![
1385                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1386                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1387                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1388                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1389                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1390                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1391                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1392                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1393            ]
1394        );
1395
1396        // Both pointers move.
1397        answer_next_touch_watch_request(
1398            &mut touch_source_stream,
1399            vec![
1400                make_touch_event_with_coords(11.0, 21.0, 1),
1401                make_touch_event_with_coords(101.0, 201.0, 2),
1402            ],
1403        )
1404        .await;
1405
1406        // Wait for another `Watch`.
1407        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1408            .await;
1409        let events = read_uapi_events(locked, &input_file, &current_task);
1410
1411        assert_eq!(
1412            events,
1413            vec![
1414                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1415                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
1416                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
1417                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1418                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
1419                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
1420                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1421            ]
1422        );
1423
1424        // Pointer 1 up.
1425        answer_next_touch_watch_request(
1426            &mut touch_source_stream,
1427            vec![
1428                make_touch_event_with_phase(EventPhase::Remove, 1),
1429                make_touch_event_with_coords(102.0, 202.0, 2),
1430            ],
1431        )
1432        .await;
1433
1434        // Wait for another `Watch`.
1435        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1436            .await;
1437        let events = read_uapi_events(locked, &input_file, &current_task);
1438
1439        assert_eq!(
1440            events,
1441            vec![
1442                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1443                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1444                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1445                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 102),
1446                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 202),
1447                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1448            ]
1449        );
1450
1451        // Pointer 2 up.
1452        answer_next_touch_watch_request(
1453            &mut touch_source_stream,
1454            vec![make_touch_event_with_phase(EventPhase::Remove, 2)],
1455        )
1456        .await;
1457
1458        // Wait for another `Watch`.
1459        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1460            .await;
1461        let events = read_uapi_events(locked, &input_file, &current_task);
1462
1463        assert_eq!(
1464            events,
1465            vec![
1466                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1467                make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1468                make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1469                make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1470            ]
1471        );
1472    }
1473
1474    #[::fuchsia::test]
1475    async fn multi_event_sequence_unsorted_in_one_watch() {
1476        // Set up resources.
1477        #[allow(deprecated, reason = "pre-existing usage")]
1478        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1479        let (_input_device, input_file, mut touch_source_stream) =
1480            start_touch_input(locked, &current_task).await;
1481
1482        // Touch add for pointer 1.
1483        answer_next_touch_watch_request(
1484            &mut touch_source_stream,
1485            vec![
1486                make_touch_event_with_coords_phase_timestamp(
1487                    10.0,
1488                    20.0,
1489                    EventPhase::Change,
1490                    1,
1491                    100,
1492                ),
1493                make_touch_event_with_coords_phase_timestamp(0.0, 0.0, EventPhase::Add, 1, 1),
1494            ],
1495        )
1496        .await;
1497
1498        // Wait for another `Watch`.
1499        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1500            .await;
1501        let events = read_uapi_events(locked, &input_file, &current_task);
1502
1503        assert_eq!(
1504            events,
1505            vec![
1506                make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
1507                make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1, 1),
1508                make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0, 1),
1509                make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0, 1),
1510                make_uapi_input_event_with_timestamp(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
1511                make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
1512                make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 100),
1513                make_uapi_input_event_with_timestamp(
1514                    uapi::EV_ABS,
1515                    uapi::ABS_MT_POSITION_X,
1516                    10,
1517                    100
1518                ),
1519                make_uapi_input_event_with_timestamp(
1520                    uapi::EV_ABS,
1521                    uapi::ABS_MT_POSITION_Y,
1522                    20,
1523                    100
1524                ),
1525                make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 100),
1526            ]
1527        );
1528    }
1529
1530    #[test_case((0.0, 0.0); "origin")]
1531    #[test_case((100.7, 200.7); "above midpoint")]
1532    #[test_case((100.3, 200.3); "below midpoint")]
1533    #[test_case((100.5, 200.5); "midpoint")]
1534    #[::fuchsia::test]
1535    async fn sends_acceptable_coordinates((x, y): (f32, f32)) {
1536        // Set up resources.
1537        #[allow(deprecated, reason = "pre-existing usage")]
1538        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1539        let (_input_device, input_file, mut touch_source_stream) =
1540            start_touch_input(locked, &current_task).await;
1541
1542        // Touch add.
1543        answer_next_touch_watch_request(
1544            &mut touch_source_stream,
1545            vec![make_touch_event_with_coords_phase(x, y, EventPhase::Add, 1)],
1546        )
1547        .await;
1548
1549        // Wait for another `Watch`.
1550        answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1551            .await;
1552        let events = read_uapi_events(locked, &input_file, &current_task);
1553
1554        // Check that the reported positions are within the acceptable error. The acceptable
1555        // error is chosen to allow either rounding or truncation.
1556        const ACCEPTABLE_ERROR: f32 = 1.0;
1557        let actual_x = events
1558            .iter()
1559            .find(|event| {
1560                event.type_ == uapi::EV_ABS as u16 && event.code == uapi::ABS_MT_POSITION_X as u16
1561            })
1562            .unwrap_or_else(|| panic!("did not find `ABS_X` event in {:?}", events))
1563            .value;
1564        let actual_y = events
1565            .iter()
1566            .find(|event| {
1567                event.type_ == uapi::EV_ABS as u16 && event.code == uapi::ABS_MT_POSITION_Y as u16
1568            })
1569            .unwrap_or_else(|| panic!("did not find `ABS_Y` event in {:?}", events))
1570            .value;
1571        assert_near!(x, actual_x as f32, ACCEPTABLE_ERROR);
1572        assert_near!(y, actual_y as f32, ACCEPTABLE_ERROR);
1573    }
1574
1575    // Per the FIDL documentation for `TouchSource::Watch()`:
1576    //
1577    // > non-sample events should return an empty |TouchResponse| table to the
1578    // > server
1579    #[test_case(
1580        make_touch_event_with_phase(EventPhase::Add, 2)
1581            => matches Some(TouchResponse { response_type: Some(_), ..});
1582        "event_with_sample_yields_some_response_type")]
1583    #[test_case(
1584        TouchEvent::default() => matches Some(TouchResponse { response_type: None, ..});
1585        "event_without_sample_yields_no_response_type")]
1586    #[::fuchsia::test]
1587    async fn sends_appropriate_reply_to_touch_source_server(
1588        event: TouchEvent,
1589    ) -> Option<TouchResponse> {
1590        // Set up resources.
1591        #[allow(deprecated, reason = "pre-existing usage")]
1592        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1593        let (_input_device, _input_file, mut touch_source_stream) =
1594            start_touch_input(locked, &current_task).await;
1595
1596        // Reply to first `Watch` request.
1597        answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1598
1599        // Get response to `event`.
1600        let responses =
1601            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1602                .await;
1603
1604        // Return the value for `test_case` to match on.
1605        responses.get(0).cloned()
1606    }
1607
1608    #[test_case(fidl_fuchsia_input::Key::Escape, uapi::KEY_POWER; "Esc maps to Power")]
1609    #[test_case(fidl_fuchsia_input::Key::A, uapi::KEY_A; "A maps to A")]
1610    #[::fuchsia::test]
1611    async fn sends_keyboard_events(fkey: fidl_fuchsia_input::Key, lkey: u32) {
1612        #[allow(deprecated, reason = "pre-existing usage")]
1613        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1614        let (_keyboard_device, keyboard_file, keyboard_listener) =
1615            start_keyboard_input(locked, &current_task).await;
1616
1617        let key_event = fuiinput::KeyEvent {
1618            timestamp: Some(0),
1619            type_: Some(fuiinput::KeyEventType::Pressed),
1620            key: Some(fkey),
1621            ..Default::default()
1622        };
1623
1624        let _ = keyboard_listener.on_key_event(&key_event).await;
1625        std::mem::drop(keyboard_listener); // Close Zircon channel.
1626        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1627        assert_eq!(events.len(), 2);
1628        assert_eq!(events[0].code, lkey as u16);
1629    }
1630
1631    #[::fuchsia::test]
1632    async fn skips_unknown_keyboard_events() {
1633        #[allow(deprecated, reason = "pre-existing usage")]
1634        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1635        let (_keyboard_device, keyboard_file, keyboard_listener) =
1636            start_keyboard_input(locked, &current_task).await;
1637
1638        let key_event = fuiinput::KeyEvent {
1639            timestamp: Some(0),
1640            type_: Some(fuiinput::KeyEventType::Pressed),
1641            key: Some(fidl_fuchsia_input::Key::AcRefresh),
1642            ..Default::default()
1643        };
1644
1645        let _ = keyboard_listener.on_key_event(&key_event).await;
1646        std::mem::drop(keyboard_listener); // Close Zircon channel.
1647        let events = read_uapi_events(locked, &keyboard_file, &current_task);
1648        assert_eq!(events.len(), 0);
1649    }
1650
1651    #[::fuchsia::test]
1652    async fn sends_power_button_events() {
1653        #[allow(deprecated, reason = "pre-existing usage")]
1654        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1655        let (_input_device, input_file, buttons_listener) =
1656            start_button_input(locked, &current_task).await;
1657
1658        let power_event = MediaButtonsEvent {
1659            volume: Some(0),
1660            mic_mute: Some(false),
1661            pause: Some(false),
1662            camera_disable: Some(false),
1663            power: Some(true),
1664            function: Some(false),
1665            ..Default::default()
1666        };
1667
1668        let _ = buttons_listener.on_event(power_event).await;
1669        std::mem::drop(buttons_listener); // Close Zircon channel.
1670
1671        let events = read_uapi_events(locked, &input_file, &current_task);
1672        assert_eq!(events.len(), 2);
1673        assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1674        assert_eq!(events[0].value, 1);
1675    }
1676
1677    #[::fuchsia::test]
1678    async fn sends_function_button_events() {
1679        #[allow(deprecated, reason = "pre-existing usage")]
1680        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1681        let (_input_device, input_file, buttons_listener) =
1682            start_button_input(locked, &current_task).await;
1683
1684        let function_event = MediaButtonsEvent {
1685            volume: Some(0),
1686            mic_mute: Some(false),
1687            pause: Some(false),
1688            camera_disable: Some(false),
1689            power: Some(false),
1690            function: Some(true),
1691            ..Default::default()
1692        };
1693
1694        let _ = buttons_listener.on_event(function_event).await;
1695        std::mem::drop(buttons_listener); // Close Zircon channel.
1696
1697        let events = read_uapi_events(locked, &input_file, &current_task);
1698        assert_eq!(events.len(), 2);
1699        assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1700        assert_eq!(events[0].value, 1);
1701    }
1702
1703    #[::fuchsia::test]
1704    async fn sends_overlapping_button_events() {
1705        #[allow(deprecated, reason = "pre-existing usage")]
1706        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1707        let (_input_device, input_file, buttons_listener) =
1708            start_button_input(locked, &current_task).await;
1709
1710        let power_event = MediaButtonsEvent {
1711            volume: Some(0),
1712            mic_mute: Some(false),
1713            pause: Some(false),
1714            camera_disable: Some(false),
1715            power: Some(true),
1716            function: Some(false),
1717            ..Default::default()
1718        };
1719
1720        let function_event = MediaButtonsEvent {
1721            volume: Some(0),
1722            mic_mute: Some(false),
1723            pause: Some(false),
1724            camera_disable: Some(false),
1725            power: Some(true),
1726            function: Some(true),
1727            ..Default::default()
1728        };
1729
1730        let function_release_event = MediaButtonsEvent {
1731            volume: Some(0),
1732            mic_mute: Some(false),
1733            pause: Some(false),
1734            camera_disable: Some(false),
1735            power: Some(true),
1736            function: Some(false),
1737            ..Default::default()
1738        };
1739
1740        let power_release_event = MediaButtonsEvent {
1741            volume: Some(0),
1742            mic_mute: Some(false),
1743            pause: Some(false),
1744            camera_disable: Some(false),
1745            power: Some(false),
1746            function: Some(false),
1747            ..Default::default()
1748        };
1749
1750        let _ = buttons_listener.on_event(power_event).await;
1751        let _ = buttons_listener.on_event(function_event).await;
1752        let _ = buttons_listener.on_event(function_release_event).await;
1753        let _ = buttons_listener.on_event(power_release_event).await;
1754        std::mem::drop(buttons_listener); // Close Zircon channel.
1755
1756        let events = read_uapi_events(locked, &input_file, &current_task);
1757        assert_eq!(events.len(), 8);
1758        assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1759        assert_eq!(events[0].value, 1);
1760        assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1761        assert_eq!(events[2].value, 1);
1762        assert_eq!(events[4].code, uapi::KEY_VOLUMEDOWN as u16);
1763        assert_eq!(events[4].value, 0);
1764        assert_eq!(events[6].code, uapi::KEY_POWER as u16);
1765        assert_eq!(events[6].value, 0);
1766    }
1767
1768    #[::fuchsia::test]
1769    async fn sends_simultaneous_button_events() {
1770        #[allow(deprecated, reason = "pre-existing usage")]
1771        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1772        let (_input_device, input_file, buttons_listener) =
1773            start_button_input(locked, &current_task).await;
1774
1775        let power_and_function_event = MediaButtonsEvent {
1776            volume: Some(0),
1777            mic_mute: Some(false),
1778            pause: Some(false),
1779            camera_disable: Some(false),
1780            power: Some(true),
1781            function: Some(true),
1782            ..Default::default()
1783        };
1784
1785        let _ = buttons_listener.on_event(power_and_function_event).await;
1786        std::mem::drop(buttons_listener); // Close Zircon channel.
1787
1788        let events = read_uapi_events(locked, &input_file, &current_task);
1789        assert_eq!(events.len(), 4);
1790        assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1791        assert_eq!(events[0].value, 1);
1792        assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1793        assert_eq!(events[2].value, 1);
1794    }
1795
1796    #[test_case(1; "Scroll up")]
1797    #[test_case(-1; "Scroll down")]
1798    #[::fuchsia::test]
1799    async fn sends_mouse_wheel_events(ticks: i64) {
1800        let time = 100;
1801        let uapi_event = uapi::input_event {
1802            time: timeval_from_time(zx::MonotonicInstant::from_nanos(time)),
1803            type_: uapi::EV_REL as u16,
1804            code: uapi::REL_WHEEL as u16,
1805            value: ticks as i32,
1806        };
1807        #[allow(deprecated, reason = "pre-existing usage")]
1808        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1809        let (_mouse_device, mouse_file, mut mouse_stream) =
1810            start_mouse_input(locked, &current_task).await;
1811
1812        answer_next_mouse_watch_request(
1813            &mut mouse_stream,
1814            vec![make_mouse_wheel_event_with_timestamp(ticks, time)],
1815        )
1816        .await;
1817
1818        // Wait for another `Watch` to ensure mouse_file is done processing the other replies.
1819        // Use an empty vec, to ensure no unexpected `uapi::input_event`s are created.
1820        answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1821
1822        let events = read_uapi_events(locked, &mouse_file, &current_task);
1823        assert_eq!(events.len(), 2);
1824        assert_eq!(events[0], uapi_event);
1825    }
1826
1827    #[::fuchsia::test]
1828    async fn ignore_mouse_non_wheel_events() {
1829        let mouse_move_event = fuipointer::MouseEvent {
1830            timestamp: Some(0),
1831            pointer_sample: Some(fuipointer::MousePointerSample {
1832                device_id: Some(0),
1833                position_in_viewport: Some([50.0, 50.0]),
1834                ..Default::default()
1835            }),
1836            ..Default::default()
1837        };
1838        let mouse_click_event = fuipointer::MouseEvent {
1839            timestamp: Some(0),
1840            pointer_sample: Some(fuipointer::MousePointerSample {
1841                device_id: Some(0),
1842                pressed_buttons: Some(vec![1]),
1843                ..Default::default()
1844            }),
1845            ..Default::default()
1846        };
1847        #[allow(deprecated, reason = "pre-existing usage")]
1848        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1849        let (_mouse_device, mouse_file, mut mouse_stream) =
1850            start_mouse_input(locked, &current_task).await;
1851
1852        // Expect mouse relay to discard MouseEvents without vertical scroll.
1853        answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_move_event]).await;
1854        answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_click_event]).await;
1855
1856        // Wait for another `Watch` to ensure mouse_file is done processing the other replies.
1857        // Use an empty vec, to ensure no unexpected `uapi::input_event`s are created.
1858        answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1859
1860        let events = read_uapi_events(locked, &mouse_file, &current_task);
1861        assert_eq!(events.len(), 0);
1862    }
1863
1864    #[::fuchsia::test]
1865    async fn touch_input_initialized_with_inspect_node() {
1866        #[allow(deprecated, reason = "pre-existing usage")]
1867        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1868        let inspector = fuchsia_inspect::Inspector::default();
1869        let touch_device = InputDevice::new_touch(
1870            1200, /* screen width */
1871            720,  /* screen height */
1872            &inspector.root(),
1873        );
1874        let _file_obj = touch_device.open_test(locked, &current_task);
1875
1876        assert_data_tree!(inspector, root: {
1877            touch_device: {
1878                total_fidl_events_received_count: 0u64,
1879                total_fidl_events_ignored_count: 0u64,
1880                total_fidl_events_unexpected_count: 0u64,
1881                total_fidl_events_converted_count: 0u64,
1882                total_uapi_events_generated_count: 0u64,
1883                last_generated_uapi_event_timestamp_ns: 0i64,
1884                touch_file_0: {
1885                    fidl_events_received_count: 0u64,
1886                    fidl_events_ignored_count: 0u64,
1887                    fidl_events_unexpected_count: 0u64,
1888                    fidl_events_converted_count: 0u64,
1889                    uapi_events_generated_count: 0u64,
1890                    uapi_events_read_count: 0u64,
1891                    last_generated_uapi_event_timestamp_ns: 0i64,
1892                    last_read_uapi_event_timestamp_ns: 0i64,
1893                }
1894            }
1895        });
1896    }
1897
1898    #[::fuchsia::test]
1899    async fn touch_relay_updates_touch_inspect_status() {
1900        let inspector = fuchsia_inspect::Inspector::default();
1901        #[allow(deprecated, reason = "pre-existing usage")]
1902        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1903        let (_input_device, input_file, mut touch_source_stream) =
1904            start_touch_input_inspect(locked, &current_task, &inspector).await;
1905
1906        // Send 2 TouchEvents to proxy that should be counted as `received` by InputFile
1907        // A TouchEvent::default() has no pointer sample so these events should be discarded.
1908        match touch_source_stream.next().await {
1909            Some(Ok(TouchSourceRequest::Watch { responder, .. })) => responder
1910                .send(vec![make_empty_touch_event(), make_empty_touch_event()])
1911                .expect("failure sending Watch reply"),
1912            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1913        }
1914
1915        // Send 5 TouchEvents with pointer sample to proxy, these should be received and converted
1916        // Add/Remove events generate 5 uapi events each. Change events generate 3 uapi events each.
1917        match touch_source_stream.next().await {
1918            Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
1919                assert_matches!(responses.as_slice(), [_, _]);
1920                responder
1921                    .send(vec![
1922                        make_touch_event_with_coords_phase_timestamp(
1923                            0.0,
1924                            0.0,
1925                            EventPhase::Add,
1926                            1,
1927                            1000,
1928                        ),
1929                        make_touch_event_with_coords_phase_timestamp(
1930                            1.0,
1931                            1.0,
1932                            EventPhase::Change,
1933                            1,
1934                            2000,
1935                        ),
1936                        make_touch_event_with_coords_phase_timestamp(
1937                            2.0,
1938                            2.0,
1939                            EventPhase::Change,
1940                            1,
1941                            3000,
1942                        ),
1943                        make_touch_event_with_coords_phase_timestamp(
1944                            3.0,
1945                            3.0,
1946                            EventPhase::Change,
1947                            1,
1948                            4000,
1949                        ),
1950                        make_touch_event_with_coords_phase_timestamp(
1951                            3.0,
1952                            3.0,
1953                            EventPhase::Remove,
1954                            1,
1955                            5000,
1956                        ),
1957                    ])
1958                    .expect("failure sending Watch reply");
1959            }
1960            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1961        }
1962
1963        // Wait for next `Watch` call and verify it has five elements in `responses`.
1964        match touch_source_stream.next().await {
1965            Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
1966                assert_matches!(responses.as_slice(), [_, _, _, _, _])
1967            }
1968            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1969        }
1970
1971        let _events = read_uapi_events(locked, &input_file, &current_task);
1972        assert_data_tree!(inspector, root: {
1973            touch_device: {
1974                total_fidl_events_received_count: 7u64,
1975                total_fidl_events_ignored_count: 2u64,
1976                total_fidl_events_unexpected_count: 0u64,
1977                total_fidl_events_converted_count: 5u64,
1978                total_uapi_events_generated_count: 22u64,
1979                last_generated_uapi_event_timestamp_ns: 5000i64,
1980                touch_file_0: {
1981                    fidl_events_received_count: 7u64,
1982                    fidl_events_ignored_count: 2u64,
1983                    fidl_events_unexpected_count: 0u64,
1984                    fidl_events_converted_count: 5u64,
1985                    uapi_events_generated_count: 22u64,
1986                    uapi_events_read_count: 22u64,
1987                    last_generated_uapi_event_timestamp_ns: 5000i64,
1988                    last_read_uapi_event_timestamp_ns: 5000i64,
1989                },
1990            }
1991        });
1992    }
1993
1994    #[::fuchsia::test]
1995    async fn new_file_updates_inspect_status() {
1996        let inspector = fuchsia_inspect::Inspector::default();
1997        #[allow(deprecated, reason = "pre-existing usage")]
1998        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1999
2000        let input_device = InputDevice::new_touch(700, 700, inspector.root());
2001        let input_file_0 =
2002            input_device.open_test(locked, &current_task).expect("Failed to create input file");
2003
2004        let (touch_source_client_end, mut touch_source_stream) =
2005            fidl::endpoints::create_request_stream::<TouchSourceMarker>();
2006        let (mouse_source_client_end, _mouse_source_stream) =
2007            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
2008        let (keyboard_proxy, mut keyboard_stream) =
2009            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
2010        let view_ref_pair =
2011            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2012        let (device_registry_proxy, mut device_listener_stream) =
2013            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
2014            );
2015
2016        let (relay, relay_handle) = input_event_relay::new_input_relay();
2017        relay.start_relays(
2018            &current_task.kernel(),
2019            EventProxyMode::None,
2020            touch_source_client_end,
2021            keyboard_proxy,
2022            mouse_source_client_end,
2023            view_ref_pair.view_ref,
2024            device_registry_proxy,
2025            input_device.open_files.clone(),
2026            Default::default(),
2027            Default::default(),
2028            Some(input_device.inspect_status.clone()),
2029            None,
2030            None,
2031        );
2032
2033        let _ = init_keyboard_listener(&mut keyboard_stream).await;
2034        let _ = init_button_listeners(&mut device_listener_stream).await;
2035
2036        relay_handle.add_touch_device(
2037            0,
2038            input_device.open_files.clone(),
2039            Some(input_device.inspect_status.clone()),
2040        );
2041
2042        // Send 2 TouchEvents to proxy that should be counted as `received` by InputFile
2043        // A TouchEvent::default() has no pointer sample so these events should be discarded.
2044        match touch_source_stream.next().await {
2045            Some(Ok(TouchSourceRequest::Watch { responder, .. })) => responder
2046                .send(vec![make_empty_touch_event(), make_empty_touch_event()])
2047                .expect("failure sending Watch reply"),
2048            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2049        }
2050
2051        // Wait for next `Watch` call and verify it has two elements in `responses`.
2052        match touch_source_stream.next().await {
2053            Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
2054                assert_matches!(responses.as_slice(), [_, _]);
2055                responder.send(vec![]).expect("failure sending Watch reply");
2056            }
2057            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2058        }
2059
2060        // Verify file node & properties remain in inspect tree when file is closed
2061        input_device.open_files.lock().clear();
2062        drop(input_file_0);
2063
2064        // Open new file which should receive input_device's subsequent events
2065        let input_file_1 =
2066            input_device.open_test(locked, &current_task).expect("Failed to create input file");
2067
2068        // Send 5 TouchEvents with pointer sample to proxy, these should be received and converted
2069        // Add/Remove events generate 5 uapi events each. Change events generate 3 uapi events each.
2070        match touch_source_stream.next().await {
2071            Some(Ok(TouchSourceRequest::Watch { responder, .. })) => {
2072                responder
2073                    .send(vec![
2074                        make_touch_event_with_coords_phase_timestamp(
2075                            0.0,
2076                            0.0,
2077                            EventPhase::Add,
2078                            1,
2079                            1000,
2080                        ),
2081                        make_touch_event_with_coords_phase_timestamp(
2082                            1.0,
2083                            1.0,
2084                            EventPhase::Change,
2085                            1,
2086                            2000,
2087                        ),
2088                        make_touch_event_with_coords_phase_timestamp(
2089                            2.0,
2090                            2.0,
2091                            EventPhase::Change,
2092                            1,
2093                            3000,
2094                        ),
2095                        make_touch_event_with_coords_phase_timestamp(
2096                            3.0,
2097                            3.0,
2098                            EventPhase::Change,
2099                            1,
2100                            4000,
2101                        ),
2102                        make_touch_event_with_coords_phase_timestamp(
2103                            3.0,
2104                            3.0,
2105                            EventPhase::Remove,
2106                            1,
2107                            5000,
2108                        ),
2109                    ])
2110                    .expect("failure sending Watch reply");
2111            }
2112            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2113        }
2114
2115        // Wait for next `Watch` call and verify it has five elements in `responses`.
2116        match touch_source_stream.next().await {
2117            Some(Ok(TouchSourceRequest::Watch { responses, .. })) => {
2118                assert_matches!(responses.as_slice(), [_, _, _, _, _])
2119            }
2120            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2121        }
2122
2123        let _events = read_uapi_events(locked, &input_file_1, &current_task);
2124
2125        // Verify file node & properties remain in inspect tree when file is closed
2126        input_device.open_files.lock().clear();
2127        drop(input_file_1);
2128
2129        assert_data_tree!(inspector, root: {
2130            touch_device: {
2131                total_fidl_events_received_count: 7u64,
2132                total_fidl_events_ignored_count: 2u64,
2133                total_fidl_events_unexpected_count: 0u64,
2134                total_fidl_events_converted_count: 5u64,
2135                total_uapi_events_generated_count: 22u64,
2136                last_generated_uapi_event_timestamp_ns: 5000i64,
2137                touch_file_0: {
2138                    fidl_events_received_count: 2u64,
2139                    fidl_events_ignored_count: 2u64,
2140                    fidl_events_unexpected_count: 0u64,
2141                    fidl_events_converted_count: 0u64,
2142                    uapi_events_generated_count: 0u64,
2143                    uapi_events_read_count: 0u64,
2144                    last_generated_uapi_event_timestamp_ns: 0i64,
2145                    last_read_uapi_event_timestamp_ns: 0i64,
2146                },
2147                touch_file_1: {
2148                    fidl_events_received_count: 5u64,
2149                    fidl_events_ignored_count: 0u64,
2150                    fidl_events_unexpected_count: 0u64,
2151                    fidl_events_converted_count: 5u64,
2152                    uapi_events_generated_count: 22u64,
2153                    uapi_events_read_count: 22u64,
2154                    last_generated_uapi_event_timestamp_ns: 5000i64,
2155                    last_read_uapi_event_timestamp_ns: 5000i64,
2156                },
2157            }
2158        });
2159    }
2160
2161    #[::fuchsia::test]
2162    async fn keyboard_input_initialized_with_inspect_node() {
2163        #[allow(deprecated, reason = "pre-existing usage")]
2164        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2165        let inspector = fuchsia_inspect::Inspector::default();
2166        let keyboard_device = InputDevice::new_keyboard(&inspector.root());
2167        let _file_obj = keyboard_device.open_test(locked, &current_task);
2168
2169        assert_data_tree!(inspector, root: {
2170            keyboard_device: {
2171                total_fidl_events_received_count: 0u64,
2172                total_fidl_events_ignored_count: 0u64,
2173                total_fidl_events_unexpected_count: 0u64,
2174                total_fidl_events_converted_count: 0u64,
2175                total_uapi_events_generated_count: 0u64,
2176                last_generated_uapi_event_timestamp_ns: 0i64,
2177                keyboard_file_0: {
2178                    fidl_events_received_count: 0u64,
2179                    fidl_events_ignored_count: 0u64,
2180                    fidl_events_unexpected_count: 0u64,
2181                    fidl_events_converted_count: 0u64,
2182                    uapi_events_generated_count: 0u64,
2183                    uapi_events_read_count: 0u64,
2184                    last_generated_uapi_event_timestamp_ns: 0i64,
2185                    last_read_uapi_event_timestamp_ns: 0i64,
2186                }
2187            }
2188        });
2189    }
2190
2191    #[::fuchsia::test]
2192    async fn button_relay_updates_keyboard_inspect_status() {
2193        let inspector = fuchsia_inspect::Inspector::default();
2194        #[allow(deprecated, reason = "pre-existing usage")]
2195        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2196        let (_input_device, input_file, buttons_listener) =
2197            start_button_input_inspect(locked, &current_task, &inspector).await;
2198
2199        // Each of these events should count toward received and converted.
2200        // They also generate 2 uapi events each.
2201        let power_event = MediaButtonsEvent {
2202            volume: Some(0),
2203            mic_mute: Some(false),
2204            pause: Some(false),
2205            camera_disable: Some(false),
2206            power: Some(true),
2207            function: Some(false),
2208            ..Default::default()
2209        };
2210
2211        let power_release_event = MediaButtonsEvent {
2212            volume: Some(0),
2213            mic_mute: Some(false),
2214            pause: Some(false),
2215            camera_disable: Some(false),
2216            power: Some(false),
2217            function: Some(false),
2218            ..Default::default()
2219        };
2220
2221        let _ = buttons_listener.on_event(power_event).await;
2222        let _ = buttons_listener.on_event(power_release_event).await;
2223
2224        let events = read_uapi_events(locked, &input_file, &current_task);
2225        assert_eq!(events.len(), 4);
2226        assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2227        assert_eq!(events[0].value, 1);
2228        assert_eq!(events[2].code, uapi::KEY_POWER as u16);
2229        assert_eq!(events[2].value, 0);
2230
2231        let _events = read_uapi_events(locked, &input_file, &current_task);
2232
2233        assert_data_tree!(inspector, root: {
2234            keyboard_device: {
2235                total_fidl_events_received_count: 2u64,
2236                total_fidl_events_ignored_count: 0u64,
2237                total_fidl_events_unexpected_count: 0u64,
2238                total_fidl_events_converted_count: 2u64,
2239                total_uapi_events_generated_count: 4u64,
2240                // Button events perform a realtime clockread, so any value will do.
2241                last_generated_uapi_event_timestamp_ns: AnyProperty,
2242                keyboard_file_0: {
2243                    fidl_events_received_count: 2u64,
2244                    fidl_events_ignored_count: 0u64,
2245                    fidl_events_unexpected_count: 0u64,
2246                    fidl_events_converted_count: 2u64,
2247                    uapi_events_generated_count: 4u64,
2248                    uapi_events_read_count: 4u64,
2249                    // Button events perform a realtime clockread, so any value will do.
2250                    last_generated_uapi_event_timestamp_ns: AnyProperty,
2251                    last_read_uapi_event_timestamp_ns: AnyProperty,
2252                },
2253            }
2254        });
2255    }
2256
2257    #[::fuchsia::test]
2258    async fn mouse_input_initialized_with_inspect_node() {
2259        #[allow(deprecated, reason = "pre-existing usage")]
2260        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2261        let inspector = fuchsia_inspect::Inspector::default();
2262        let mouse_device = InputDevice::new_mouse(&inspector.root());
2263        let _file_obj = mouse_device.open_test(locked, &current_task);
2264
2265        assert_data_tree!(inspector, root: {
2266            mouse_device: {
2267                total_fidl_events_received_count: 0u64,
2268                total_fidl_events_ignored_count: 0u64,
2269                total_fidl_events_unexpected_count: 0u64,
2270                total_fidl_events_converted_count: 0u64,
2271                total_uapi_events_generated_count: 0u64,
2272                last_generated_uapi_event_timestamp_ns: 0i64,
2273                mouse_file_0: {
2274                    fidl_events_received_count: 0u64,
2275                    fidl_events_ignored_count: 0u64,
2276                    fidl_events_unexpected_count: 0u64,
2277                    fidl_events_converted_count: 0u64,
2278                    uapi_events_generated_count: 0u64,
2279                    uapi_events_read_count: 0u64,
2280                    last_generated_uapi_event_timestamp_ns: 0i64,
2281                    last_read_uapi_event_timestamp_ns: 0i64,
2282                }
2283            }
2284        });
2285    }
2286
2287    #[::fuchsia::test]
2288    async fn mouse_relay_updates_mouse_inspect_status() {
2289        let inspector = fuchsia_inspect::Inspector::default();
2290        #[allow(deprecated, reason = "pre-existing usage")]
2291        let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
2292        let (_input_device, input_file, mut mouse_source_stream) =
2293            start_mouse_input_inspect(locked, &current_task, &inspector).await;
2294
2295        let mouse_move_event = fuipointer::MouseEvent {
2296            timestamp: Some(0),
2297            pointer_sample: Some(fuipointer::MousePointerSample {
2298                device_id: Some(0),
2299                position_in_viewport: Some([50.0, 50.0]),
2300                scroll_v: Some(0),
2301                ..Default::default()
2302            }),
2303            ..Default::default()
2304        };
2305        let mouse_click_event = fuipointer::MouseEvent {
2306            timestamp: Some(0),
2307            pointer_sample: Some(fuipointer::MousePointerSample {
2308                device_id: Some(0),
2309                scroll_v: Some(0),
2310                pressed_buttons: Some(vec![1]),
2311                ..Default::default()
2312            }),
2313            ..Default::default()
2314        };
2315
2316        // Send 2 non-wheel MouseEvents to proxy that should be counted as `received` by InputFile
2317        // These events have no scroll_v delta in the pointer sample so they should be ignored.
2318        answer_next_mouse_watch_request(
2319            &mut mouse_source_stream,
2320            vec![mouse_move_event, mouse_click_event],
2321        )
2322        .await;
2323
2324        // Send 5 MouseEvents with non-zero scroll_v delta to proxy, these should be received and
2325        // converted to 1 uapi event each, with an extra sync event to signify end of the batch.
2326        answer_next_mouse_watch_request(
2327            &mut mouse_source_stream,
2328            (0..5).map(|_| make_mouse_wheel_event(1)).collect(),
2329        )
2330        .await;
2331
2332        // Send a final mouse wheel event and ensure the inspect tree reflects it's timestamp under
2333        // last_generated_uapi_event_timestamp_ns and last_read_uapi_event_timestamp_ns.
2334        answer_next_mouse_watch_request(
2335            &mut mouse_source_stream,
2336            vec![make_mouse_wheel_event_with_timestamp(-1, 5000)],
2337        )
2338        .await;
2339
2340        // Wait for another `Watch` to ensure mouse_file is done processing the other replies.
2341        // Use an empty vec, to ensure no unexpected `uapi::input_event`s are created.
2342        answer_next_mouse_watch_request(&mut mouse_source_stream, vec![]).await;
2343
2344        let _events = read_uapi_events(locked, &input_file, &current_task);
2345        assert_data_tree!(inspector, root: {
2346            mouse_device: {
2347                total_fidl_events_received_count: 8u64,
2348                total_fidl_events_ignored_count: 2u64,
2349                total_fidl_events_unexpected_count: 0u64,
2350                total_fidl_events_converted_count: 6u64,
2351                total_uapi_events_generated_count: 8u64,
2352                last_generated_uapi_event_timestamp_ns: 5000i64,
2353                mouse_file_0: {
2354                    fidl_events_received_count: 8u64,
2355                    fidl_events_ignored_count: 2u64,
2356                    fidl_events_unexpected_count: 0u64,
2357                    fidl_events_converted_count: 6u64,
2358                    uapi_events_generated_count: 8u64,
2359                    uapi_events_read_count: 8u64,
2360                    last_generated_uapi_event_timestamp_ns: 5000i64,
2361                    last_read_uapi_event_timestamp_ns: 5000i64,
2362                },
2363            }
2364        });
2365    }
2366}