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