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().on_file_opened(&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::endpoints::RequestStream as _;
386    use fidl_fuchsia_ui_input::MediaButtonsEvent;
387    use fidl_fuchsia_ui_input3 as fuiinput;
388    use fidl_fuchsia_ui_pointer as fuipointer;
389    use fidl_fuchsia_ui_policy as fuipolicy;
390    use fuipointer::{
391        EventPhase, TouchEvent, TouchInteractionId, TouchPointerSample, TouchSourceV2Marker,
392    };
393    use futures::StreamExt as _;
394    use pretty_assertions::assert_eq;
395    use starnix_core::mm::{MemoryAccessor as _, MemoryAccessorExt as _};
396    use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
397    use starnix_core::task::{EventHandler, Waiter};
398    use starnix_core::testing::{map_memory, spawn_kernel_and_run};
399    use starnix_core::vfs::FileHandle;
400    use starnix_core::vfs::buffers::VecOutputBuffer;
401    use starnix_syscalls::SUCCESS;
402    use starnix_types::time::timeval_from_time;
403    use starnix_uapi::errors::EAGAIN;
404    use starnix_uapi::uapi;
405    use starnix_uapi::user_address::UserAddress;
406    use starnix_uapi::vfs::FdEvents;
407    use test_case::test_case;
408    use test_util::assert_near;
409    use zerocopy::FromBytes as _;
410
411    const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
412
413    async fn start_touch_input(
414        current_task: &CurrentTask,
415    ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
416        let inspector = fuchsia_inspect::Inspector::default();
417        start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
418    }
419
420    async fn start_touch_input_inspect(
421        current_task: &CurrentTask,
422        inspector: &fuchsia_inspect::Inspector,
423    ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
424        start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
425    }
426
427    async fn init_keyboard_listener(
428        keyboard_stream: &mut fuiinput::KeyboardRequestStream,
429    ) -> fuiinput::KeyboardListenerProxy {
430        let keyboard_listener = match keyboard_stream.next().await {
431            Some(Ok(fuiinput::KeyboardRequest::AddListener {
432                view_ref: _,
433                listener,
434                responder,
435            })) => {
436                let _ = responder.send();
437                listener.into_proxy()
438            }
439            _ => {
440                panic!("Failed to get event");
441            }
442        };
443
444        keyboard_listener
445    }
446
447    async fn init_button_listeners(
448        device_listener_stream: &mut fuipolicy::DeviceListenerRegistryRequestStream,
449    ) -> (fuipolicy::MediaButtonsListenerProxy, fuipolicy::TouchButtonsListenerProxy) {
450        let media_buttons_listener = match device_listener_stream.next().await {
451            Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
452                listener,
453                responder,
454            })) => {
455                let _ = responder.send();
456                listener.into_proxy()
457            }
458            _ => {
459                panic!("Failed to get event");
460            }
461        };
462
463        let touch_buttons_listener = match device_listener_stream.next().await {
464            Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
465                listener,
466                responder,
467            })) => {
468                let _ = responder.send();
469                listener.into_proxy()
470            }
471            _ => {
472                panic!("Failed to get event");
473            }
474        };
475
476        (media_buttons_listener, touch_buttons_listener)
477    }
478
479    async fn start_touch_input_inspect_and_dimensions(
480        current_task: &CurrentTask,
481        x_max: i32,
482        y_max: i32,
483        inspector: &fuchsia_inspect::Inspector,
484    ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
485        let input_device = InputDevice::new_touch(x_max, y_max, inspector.root());
486        let input_file = input_device.open_test(current_task).expect("Failed to create input file");
487
488        let (touch_source_client_end, touch_source_stream) =
489            fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
490
491        let (mouse_source_client_end, _mouse_source_stream) =
492            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
493
494        let (keyboard_proxy, mut keyboard_stream) =
495            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
496        let view_ref_pair =
497            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
498
499        let (device_registry_proxy, mut device_listener_stream) =
500            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
501            );
502
503        let (relay, _relay_handle) = input_event_relay::new_input_relay();
504        relay.start_relays(
505            &current_task.kernel(),
506            EventProxyMode::None,
507            touch_source_client_end,
508            keyboard_proxy,
509            mouse_source_client_end,
510            view_ref_pair.view_ref,
511            device_registry_proxy,
512            input_device.open_files.clone(),
513            Default::default(),
514            None,
515            Some(input_device.inspect_status.clone()),
516            None,
517        );
518
519        let _ = init_keyboard_listener(&mut keyboard_stream).await;
520        let _ = init_button_listeners(&mut device_listener_stream).await;
521
522        (input_device, input_file, touch_source_stream)
523    }
524
525    async fn start_keyboard_input(
526        current_task: &CurrentTask,
527    ) -> (InputDevice, FileHandle, fuiinput::KeyboardListenerProxy) {
528        let inspector = fuchsia_inspect::Inspector::default();
529        start_keyboard_input_inspect(current_task, &inspector).await
530    }
531
532    async fn start_keyboard_input_inspect(
533        current_task: &CurrentTask,
534        inspector: &fuchsia_inspect::Inspector,
535    ) -> (InputDevice, FileHandle, fuiinput::KeyboardListenerProxy) {
536        let input_device = InputDevice::new_keyboard(inspector.root());
537        let input_file = input_device.open_test(current_task).expect("Failed to create input file");
538        let (keyboard_proxy, mut keyboard_stream) =
539            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
540        let view_ref_pair =
541            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
542
543        let (device_registry_proxy, mut device_listener_stream) =
544            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
545            );
546
547        let (touch_source_client_end, _touch_source_stream) =
548            fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
549
550        let (mouse_source_client_end, _mouse_source_stream) =
551            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
552
553        let (relay, _relay_handle) = input_event_relay::new_input_relay();
554        relay.start_relays(
555            current_task.kernel(),
556            EventProxyMode::None,
557            touch_source_client_end,
558            keyboard_proxy,
559            mouse_source_client_end,
560            view_ref_pair.view_ref,
561            device_registry_proxy,
562            Default::default(),
563            input_device.open_files.clone(),
564            None,
565            None,
566            Some(input_device.inspect_status.clone()),
567        );
568
569        let keyboad_listener = init_keyboard_listener(&mut keyboard_stream).await;
570        let _ = init_button_listeners(&mut device_listener_stream).await;
571
572        (input_device, input_file, keyboad_listener)
573    }
574
575    async fn start_button_input(
576        current_task: &CurrentTask,
577    ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
578        let inspector = fuchsia_inspect::Inspector::default();
579        start_button_input_inspect(current_task, &inspector).await
580    }
581
582    async fn start_button_input_inspect(
583        current_task: &CurrentTask,
584        inspector: &fuchsia_inspect::Inspector,
585    ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
586        let input_device = InputDevice::new_keyboard(inspector.root());
587        let input_file = input_device.open_test(current_task).expect("Failed to create input file");
588        let (device_registry_proxy, mut device_listener_stream) =
589            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
590            );
591
592        let (touch_source_client_end, _touch_source_stream) =
593            fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
594        let (mouse_source_client_end, _mouse_source_stream) =
595            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
596        let (keyboard_proxy, mut keyboard_stream) =
597            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
598        let view_ref_pair =
599            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
600
601        let (relay, _relay_handle) = input_event_relay::new_input_relay();
602        relay.start_relays(
603            current_task.kernel(),
604            EventProxyMode::None,
605            touch_source_client_end,
606            keyboard_proxy,
607            mouse_source_client_end,
608            view_ref_pair.view_ref,
609            device_registry_proxy,
610            Default::default(),
611            input_device.open_files.clone(),
612            None,
613            None,
614            Some(input_device.inspect_status.clone()),
615        );
616
617        let _ = init_keyboard_listener(&mut keyboard_stream).await;
618        let (button_listener, _) = init_button_listeners(&mut device_listener_stream).await;
619
620        (input_device, input_file, button_listener)
621    }
622
623    async fn start_mouse_input(
624        current_task: &CurrentTask,
625    ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
626        let inspector = fuchsia_inspect::Inspector::default();
627        start_mouse_input_inspect(current_task, &inspector).await
628    }
629
630    async fn start_mouse_input_inspect(
631        current_task: &CurrentTask,
632        inspector: &fuchsia_inspect::Inspector,
633    ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
634        let input_device = InputDevice::new_mouse(inspector.root());
635        let input_file = input_device.open_test(current_task).expect("Failed to create input file");
636
637        let (touch_source_client_end, _touch_source_stream) =
638            fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
639
640        let (mouse_source_client_end, mouse_source_stream) =
641            fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
642
643        let (keyboard_proxy, mut keyboard_stream) =
644            fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
645        let view_ref_pair =
646            fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
647
648        let (device_registry_proxy, mut device_listener_stream) =
649            fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
650            );
651
652        let (relay, _relay_handle) = input_event_relay::new_input_relay();
653        relay.start_relays(
654            &current_task.kernel(),
655            EventProxyMode::None,
656            touch_source_client_end,
657            keyboard_proxy,
658            mouse_source_client_end,
659            view_ref_pair.view_ref,
660            device_registry_proxy,
661            Default::default(),
662            Default::default(),
663            Some(input_device.clone()),
664            None,
665            None,
666        );
667
668        let _ = init_keyboard_listener(&mut keyboard_stream).await;
669        let _ = init_button_listeners(&mut device_listener_stream).await;
670
671        (input_device, input_file, mouse_source_stream)
672    }
673
674    fn make_touch_event(pointer_id: u32) -> fuipointer::TouchEvent {
675        // Default to `Change`, because that has the fewest side effects.
676        make_touch_event_with_phase(EventPhase::Change, pointer_id)
677    }
678
679    fn make_touch_event_with_phase(phase: EventPhase, pointer_id: u32) -> fuipointer::TouchEvent {
680        make_touch_event_with_coords_phase(0.0, 0.0, phase, pointer_id)
681    }
682
683    fn make_touch_event_with_coords_phase(
684        x: f32,
685        y: f32,
686        phase: EventPhase,
687        pointer_id: u32,
688    ) -> fuipointer::TouchEvent {
689        make_touch_event_with_coords_phase_timestamp(x, y, phase, pointer_id, 0)
690    }
691
692    fn make_touch_event_with_coords(x: f32, y: f32, pointer_id: u32) -> fuipointer::TouchEvent {
693        make_touch_event_with_coords_phase(x, y, EventPhase::Change, pointer_id)
694    }
695
696    fn make_touch_event_with_coords_phase_timestamp(
697        x: f32,
698        y: f32,
699        phase: EventPhase,
700        pointer_id: u32,
701        time_nanos: i64,
702    ) -> fuipointer::TouchEvent {
703        make_touch_event_with_coords_phase_timestamp_device_id(
704            x, y, phase, pointer_id, time_nanos, 0,
705        )
706    }
707
708    fn make_empty_touch_event() -> fuipointer::TouchEvent {
709        TouchEvent {
710            pointer_sample: Some(TouchPointerSample {
711                interaction: Some(TouchInteractionId {
712                    pointer_id: 0,
713                    device_id: 0,
714                    interaction_id: 0,
715                }),
716                ..Default::default()
717            }),
718            ..Default::default()
719        }
720    }
721
722    fn make_touch_event_with_coords_phase_timestamp_device_id(
723        x: f32,
724        y: f32,
725        phase: EventPhase,
726        pointer_id: u32,
727        time_nanos: i64,
728        device_id: u32,
729    ) -> fuipointer::TouchEvent {
730        TouchEvent {
731            timestamp: Some(time_nanos),
732            pointer_sample: Some(TouchPointerSample {
733                position_in_viewport: Some([x, y]),
734                // Default to `Change`, because that has the fewest side effects.
735                phase: Some(phase),
736                interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
737                ..Default::default()
738            }),
739            ..Default::default()
740        }
741    }
742
743    fn make_mouse_wheel_event(ticks: i64) -> fuipointer::MouseEvent {
744        make_mouse_wheel_event_with_timestamp(ticks, 0)
745    }
746
747    fn make_mouse_wheel_event_with_timestamp(ticks: i64, timestamp: i64) -> fuipointer::MouseEvent {
748        fuipointer::MouseEvent {
749            timestamp: Some(timestamp),
750            pointer_sample: Some(fuipointer::MousePointerSample {
751                device_id: Some(0),
752                scroll_v: Some(ticks),
753                ..Default::default()
754            }),
755            ..Default::default()
756        }
757    }
758
759    fn read_uapi_events(file: &FileHandle, current_task: &CurrentTask) -> Vec<uapi::input_event> {
760        std::iter::from_fn(|| {
761            let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
762            match file.read(current_task, &mut event_bytes) {
763                Ok(INPUT_EVENT_SIZE) => Some(
764                    uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
765                        .map_err(|_| anyhow!("failed to read input_event from buffer")),
766                ),
767                Ok(other_size) => {
768                    Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
769                }
770                Err(Errno { code: EAGAIN, .. }) => None,
771                Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
772            }
773        })
774        .enumerate()
775        .map(|(i, read_res)| match read_res {
776            Ok(event) => event,
777            Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
778        })
779        .collect()
780    }
781
782    // Sends `touch_events` to the client stream and waits for `AcknowledgeEvents`.
783    async fn answer_next_touch_watch_request(
784        request_stream: &mut fuipointer::TouchSourceV2RequestStream,
785        touch_events: Vec<TouchEvent>,
786    ) {
787        let control_handle = request_stream.control_handle();
788        control_handle
789            .send_on_touch_events(touch_events, 1)
790            .expect("failure sending OnTouchEvents");
791        match request_stream.next().await {
792            Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents { .. })) => {}
793            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
794        }
795    }
796
797    // Sends `mouse_events` to the client stream and waits for `AcknowledgeEvents`.
798    async fn answer_next_mouse_watch_request(
799        request_stream: &mut fuipointer::MouseSourceV2RequestStream,
800        mouse_events: Vec<fuipointer::MouseEvent>,
801    ) {
802        let control_handle = request_stream.control_handle();
803        control_handle
804            .send_on_mouse_events(mouse_events, 1)
805            .expect("failure sending OnMouseEvents");
806        match request_stream.next().await {
807            Some(Ok(fuipointer::MouseSourceV2Request::AcknowledgeEvents { .. })) => {}
808            unexpected_request => panic!("unexpected request {:?}", unexpected_request),
809        }
810    }
811
812    #[::fuchsia::test]
813    async fn touch_source_v2_acknowledges_events_with_matching_stamp() {
814        spawn_kernel_and_run(async move |current_task| {
815            let (_input_device, _input_file, mut touch_source_stream) =
816                start_touch_input(&current_task).await;
817
818            let control_handle = touch_source_stream.control_handle();
819            control_handle
820                .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 10)
821                .expect("failure sending OnTouchEvents");
822            match touch_source_stream.next().await {
823                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
824                    last_acknowledged_event_stamp,
825                    ..
826                })) => assert_eq!(last_acknowledged_event_stamp, 10),
827                unexpected => panic!("unexpected request {:?}", unexpected),
828            }
829
830            control_handle
831                .send_on_touch_events(
832                    vec![
833                        make_empty_touch_event(),
834                        make_empty_touch_event(),
835                        make_empty_touch_event(),
836                        make_empty_touch_event(),
837                        make_empty_touch_event(),
838                    ],
839                    20,
840                )
841                .expect("failure sending OnTouchEvents");
842            match touch_source_stream.next().await {
843                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
844                    last_acknowledged_event_stamp,
845                    ..
846                })) => assert_eq!(last_acknowledged_event_stamp, 20),
847                unexpected => panic!("unexpected request {:?}", unexpected),
848            }
849        })
850        .await;
851    }
852
853    #[::fuchsia::test]
854    async fn notifies_polling_waiters_of_new_data() {
855        spawn_kernel_and_run(async move |current_task| {
856            // Set up resources.
857
858            let (_input_device, input_file, mut touch_source_stream) =
859                start_touch_input(&current_task).await;
860            let waiter1 = Waiter::new();
861            let waiter2 = Waiter::new();
862
863            // Ask `input_file` to notify waiters when data is available to read.
864            [&waiter1, &waiter2].iter().for_each(|waiter| {
865                input_file.wait_async(&current_task, waiter, FdEvents::POLLIN, EventHandler::None);
866            });
867            assert_matches!(waiter1.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
868            assert_matches!(waiter2.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
869
870            // Reply to first `Watch` request.
871            answer_next_touch_watch_request(
872                &mut touch_source_stream,
873                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
874            )
875            .await;
876            answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
877                .await;
878
879            // `InputFile` should be done processing the first reply, since it has sent its second
880            // request. And, as part of processing the first reply, `InputFile` should have notified
881            // the interested waiters.
882            assert_eq!(waiter1.wait_until(&current_task, zx::MonotonicInstant::ZERO), Ok(()));
883            assert_eq!(waiter2.wait_until(&current_task, zx::MonotonicInstant::ZERO), Ok(()));
884        })
885        .await;
886    }
887
888    #[::fuchsia::test]
889    async fn notifies_blocked_waiter_of_new_data() {
890        spawn_kernel_and_run(async move |current_task| {
891            // Set up resources.
892            let kernel = current_task.kernel().clone();
893            let (_input_device, input_file, mut touch_source_stream) =
894                start_touch_input(&current_task).await;
895            let waiter = Waiter::new();
896
897            // Ask `input_file` to notify `waiter` when data is available to read.
898            input_file.wait_async(&current_task, &waiter, FdEvents::POLLIN, EventHandler::None);
899
900            let closure = move |task: &CurrentTask| waiter.wait(&task);
901
902            let (waiter_thread, req) = SpawnRequestBuilder::new()
903                .with_debug_name("input-device-waiter")
904                .with_sync_closure(closure)
905                .build_with_async_result();
906            kernel.kthreads.spawner().spawn_from_request(req);
907
908            let mut waiter_thread = Box::pin(waiter_thread);
909            assert_matches!(futures::poll!(&mut waiter_thread), futures::task::Poll::Pending);
910
911            // Reply to first `Watch` request.
912            answer_next_touch_watch_request(
913                &mut touch_source_stream,
914                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
915            )
916            .await;
917
918            // Wait for another `Watch`.
919            //
920            // TODO(https://fxbug.dev/42075452): Without this, `relay_thread` gets stuck `await`-ing
921            // the reply to its first request. Figure out why that happens, and remove this second
922            // reply.
923            answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
924                .await;
925        })
926        .await;
927    }
928
929    #[::fuchsia::test]
930    async fn does_not_notify_polling_waiters_without_new_data() {
931        spawn_kernel_and_run(async move |current_task| {
932            // Set up resources.
933
934            let (_input_device, input_file, mut touch_source_stream) =
935                start_touch_input(&current_task).await;
936            let waiter1 = Waiter::new();
937            let waiter2 = Waiter::new();
938
939            // Ask `input_file` to notify waiters when data is available to read.
940            [&waiter1, &waiter2].iter().for_each(|waiter| {
941                input_file.wait_async(&current_task, waiter, FdEvents::POLLIN, EventHandler::None);
942            });
943            assert_matches!(waiter1.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
944            assert_matches!(waiter2.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
945
946            // Reply to first `Watch` request with an empty set of events.
947            answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
948
949            // `InputFile` should be done processing the first reply. Since there
950            // were no touch_events given, `InputFile` should not have notified the
951            // interested waiters.
952            assert_matches!(waiter1.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
953            assert_matches!(waiter2.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
954        })
955        .await;
956    }
957
958    // Note: a user program may also want to be woken if events were already ready at the
959    // time that the program called `epoll_wait()`. However, there's no test for that case
960    // in this module, because:
961    //
962    // 1. Not all programs will want to be woken in such a case. In particular, some programs
963    //    use "edge-triggered" mode instead of "level-tiggered" mode. For details on the
964    //    two modes, see https://man7.org/linux/man-pages/man7/epoll.7.html.
965    // 2. For programs using "level-triggered" mode, the relevant behavior is implemented in
966    //    the `epoll` module, and verified by `epoll::tests::test_epoll_ready_then_wait()`.
967    //
968    // See also: the documentation for `FileOps::wait_async()`.
969
970    #[::fuchsia::test]
971    async fn honors_wait_cancellation() {
972        spawn_kernel_and_run(async move |current_task| {
973            // Set up input resources.
974
975            let (_input_device, input_file, mut touch_source_stream) =
976                start_touch_input(&current_task).await;
977            let waiter1 = Waiter::new();
978            let waiter2 = Waiter::new();
979
980            // Ask `input_file` to notify `waiter` when data is available to read.
981            let waitkeys = [&waiter1, &waiter2]
982                .iter()
983                .map(|waiter| {
984                    input_file
985                        .wait_async(&current_task, waiter, FdEvents::POLLIN, EventHandler::None)
986                        .expect("wait_async")
987                })
988                .collect::<Vec<_>>();
989
990            // Cancel wait for `waiter1`.
991            waitkeys.into_iter().next().expect("failed to get first waitkey").cancel();
992
993            // Reply to first `Watch` request.
994            answer_next_touch_watch_request(
995                &mut touch_source_stream,
996                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
997            )
998            .await;
999            // Wait for another `Watch`.
1000            answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1001                .await;
1002
1003            // `InputFile` should be done processing the first reply, since it has sent its second
1004            // request. And, as part of processing the first reply, `InputFile` should have notified
1005            // the interested waiters.
1006            assert_matches!(waiter1.wait_until(&current_task, zx::MonotonicInstant::ZERO), Err(_));
1007            assert_eq!(waiter2.wait_until(&current_task, zx::MonotonicInstant::ZERO), Ok(()));
1008        })
1009        .await;
1010    }
1011
1012    #[::fuchsia::test]
1013    async fn query_events() {
1014        spawn_kernel_and_run(async move |current_task| {
1015            // Set up resources.
1016
1017            let (_input_device, input_file, mut touch_source_stream) =
1018                start_touch_input(&current_task).await;
1019
1020            // Check initial expectation.
1021            assert_eq!(
1022                input_file.query_events(&current_task).expect("query_events"),
1023                FdEvents::empty(),
1024                "events should be empty before data arrives"
1025            );
1026
1027            // Reply to first `Watch` request.
1028            answer_next_touch_watch_request(
1029                &mut touch_source_stream,
1030                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1031            )
1032            .await;
1033
1034            // Wait for another `Watch`.
1035            answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1036                .await;
1037
1038            // Check post-watch expectation.
1039            assert_eq!(
1040                input_file.query_events(&current_task).expect("query_events"),
1041                FdEvents::POLLIN | FdEvents::POLLRDNORM,
1042                "events should be POLLIN after data arrives"
1043            );
1044        })
1045        .await;
1046    }
1047
1048    fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1049        make_uapi_input_event_with_timestamp(ty, code, value, 0)
1050    }
1051
1052    fn make_uapi_input_event_with_timestamp(
1053        ty: u32,
1054        code: u32,
1055        value: i32,
1056        time_nanos: i64,
1057    ) -> uapi::input_event {
1058        uapi::input_event {
1059            time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
1060            type_: ty as u16,
1061            code: code as u16,
1062            value,
1063        }
1064    }
1065
1066    #[::fuchsia::test]
1067    async fn touch_event_ignored() {
1068        spawn_kernel_and_run(async move |current_task| {
1069            // Set up resources.
1070            let inspector = fuchsia_inspect::Inspector::default();
1071
1072            let (_input_device, input_file, mut touch_source_stream) =
1073                start_touch_input_inspect(&current_task, &inspector).await;
1074
1075            // Touch add for pointer 1. This should be counted as a received event and a converted
1076            // event. It should also yield 6 generated events.
1077            answer_next_touch_watch_request(
1078                &mut touch_source_stream,
1079                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1080            )
1081            .await;
1082
1083            // Wait for another `Watch` to ensure input_file done processing the first reply.
1084            // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1085            // `uapi::input_event`s. This should be counted as a received event and an ignored event.
1086            answer_next_touch_watch_request(
1087                &mut touch_source_stream,
1088                vec![make_empty_touch_event()],
1089            )
1090            .await;
1091
1092            // Consume all of the `uapi::input_event`s that are available.
1093            let events = read_uapi_events(&input_file, &current_task);
1094
1095            assert_eq!(events.len(), 6);
1096
1097            // Reply to `Watch` request of empty event. This should be counted as a received event and
1098            // an ignored event.
1099            answer_next_touch_watch_request(
1100                &mut touch_source_stream,
1101                vec![make_empty_touch_event()],
1102            )
1103            .await;
1104
1105            let events = read_uapi_events(&input_file, &current_task);
1106            assert_eq!(events, vec![]);
1107            assert_data_tree!(inspector, root: {
1108                touch_device: {
1109                    active_wake_leases_count: 0u64,
1110                    total_events_with_wake_lease_count: 0u64,
1111                    total_fidl_events_received_count: 3u64,
1112                    total_fidl_events_ignored_count: 2u64,
1113                    total_fidl_events_unexpected_count: 0u64,
1114                    total_fidl_events_converted_count: 1u64,
1115                    total_uapi_events_generated_count: 6u64,
1116                    last_generated_uapi_event_timestamp_ns: 0i64,
1117                    touch_file_0: {
1118                        fidl_events_received_count: 3u64,
1119                        fidl_events_ignored_count: 2u64,
1120                        fidl_events_unexpected_count: 0u64,
1121                        fidl_events_converted_count: 1u64,
1122                        uapi_events_generated_count: 6u64,
1123                        uapi_events_read_count: 6u64,
1124                        fd_read_count: 8u64,
1125                        fd_notify_count: 1u64,
1126                        last_generated_uapi_event_timestamp_ns: 0i64,
1127                        last_read_uapi_event_timestamp_ns: 0i64,
1128                        opened_without_nonblock: AnyProperty,
1129                        open_timestamp_ns: AnyProperty,
1130                        closed: AnyProperty,
1131                        close_timestamp_ns: AnyProperty,
1132                    },
1133                }
1134            });
1135        })
1136        .await;
1137    }
1138
1139    #[test_case(make_touch_event_with_phase(EventPhase::Add, 1); "touch add for pointer already added")]
1140    #[test_case(make_touch_event_with_phase(EventPhase::Change, 2); "touch change for pointer not added")]
1141    #[test_case(make_touch_event_with_phase(EventPhase::Remove, 2); "touch remove for pointer not added")]
1142    #[test_case(make_touch_event_with_phase(EventPhase::Cancel, 1); "touch cancel")]
1143    #[::fuchsia::test]
1144    async fn touch_event_unexpected(event: TouchEvent) {
1145        spawn_kernel_and_run(async move |current_task| {
1146            // Set up resources.
1147            let inspector = fuchsia_inspect::Inspector::default();
1148
1149            let (_input_device, input_file, mut touch_source_stream) =
1150                start_touch_input_inspect(&current_task, &inspector).await;
1151
1152            // Touch add for pointer 1. This should be counted as a received event and a converted
1153            // event. It should also yield 6 generated events.
1154            answer_next_touch_watch_request(
1155                &mut touch_source_stream,
1156                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1157            )
1158            .await;
1159
1160            // Wait for another `Watch` to ensure input_file done processing the first reply.
1161            // Use an empty `TouchEvent`, to minimize the chance that this event creates unexpected
1162            // `uapi::input_event`s. This should be counted as a received event and an ignored event.
1163            answer_next_touch_watch_request(
1164                &mut touch_source_stream,
1165                vec![make_empty_touch_event()],
1166            )
1167            .await;
1168
1169            // Consume all of the `uapi::input_event`s that are available.
1170            let events = read_uapi_events(&input_file, &current_task);
1171
1172            assert_eq!(events.len(), 6);
1173
1174            // Reply to `Watch` request of given event. This should be counted as a received event and
1175            // an unexpected event.
1176            answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1177
1178            let events = read_uapi_events(&input_file, &current_task);
1179            assert_eq!(events, vec![]);
1180            assert_data_tree!(inspector, root: {
1181                touch_device: {
1182                    active_wake_leases_count: 0u64,
1183                    total_events_with_wake_lease_count: 0u64,
1184                    total_fidl_events_received_count: 3u64,
1185                    total_fidl_events_ignored_count: 1u64,
1186                    total_fidl_events_unexpected_count: 1u64,
1187                    total_fidl_events_converted_count: 1u64,
1188                    total_uapi_events_generated_count: 6u64,
1189                    last_generated_uapi_event_timestamp_ns: 0i64,
1190                    touch_file_0: {
1191                        fidl_events_received_count: 3u64,
1192                        fidl_events_ignored_count: 1u64,
1193                        fidl_events_unexpected_count: 1u64,
1194                        fidl_events_converted_count: 1u64,
1195                        uapi_events_generated_count: 6u64,
1196                        uapi_events_read_count: 6u64,
1197                        fd_read_count: 8u64,
1198                        fd_notify_count: 1u64,
1199                        last_generated_uapi_event_timestamp_ns: 0i64,
1200                        last_read_uapi_event_timestamp_ns: 0i64,
1201                        opened_without_nonblock: AnyProperty,
1202                        open_timestamp_ns: AnyProperty,
1203                        closed: AnyProperty,
1204                        close_timestamp_ns: AnyProperty,
1205                    },
1206                }
1207            });
1208        })
1209        .await;
1210    }
1211
1212    #[::fuchsia::test]
1213    async fn translates_touch_add() {
1214        spawn_kernel_and_run(async move |current_task| {
1215            // Set up resources.
1216
1217            let (_input_device, input_file, mut touch_source_stream) =
1218                start_touch_input(&current_task).await;
1219
1220            // Touch add for pointer 1.
1221            answer_next_touch_watch_request(
1222                &mut touch_source_stream,
1223                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1224            )
1225            .await;
1226
1227            // Wait for another `Watch` to ensure input_file done processing the first reply.
1228            // Use an empty `TouchEvent`, to minimize the chance that this event
1229            // creates unexpected `uapi::input_event`s.
1230            answer_next_touch_watch_request(
1231                &mut touch_source_stream,
1232                vec![make_empty_touch_event()],
1233            )
1234            .await;
1235
1236            // Consume all of the `uapi::input_event`s that are available.
1237            let events = read_uapi_events(&input_file, &current_task);
1238
1239            assert_eq!(
1240                events,
1241                vec![
1242                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1243                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1244                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1245                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1246                    make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1247                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1248                ]
1249            );
1250        })
1251        .await;
1252    }
1253
1254    #[::fuchsia::test]
1255    async fn translates_touch_change() {
1256        spawn_kernel_and_run(async move |current_task| {
1257            // Set up resources.
1258
1259            let (_input_device, input_file, mut touch_source_stream) =
1260                start_touch_input(&current_task).await;
1261
1262            // Touch add for pointer 1.
1263            answer_next_touch_watch_request(
1264                &mut touch_source_stream,
1265                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1266            )
1267            .await;
1268
1269            // Wait for another `Watch` to ensure input_file done processing the first reply.
1270            // Use an empty `TouchEvent`, to minimize the chance that this event
1271            // creates unexpected `uapi::input_event`s.
1272            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1273                .await;
1274
1275            // Consume all of the `uapi::input_event`s that are available.
1276            let events = read_uapi_events(&input_file, &current_task);
1277
1278            assert_eq!(events.len(), 6);
1279
1280            // Reply to touch change.
1281            answer_next_touch_watch_request(
1282                &mut touch_source_stream,
1283                vec![make_touch_event_with_coords(10.0, 20.0, 1)],
1284            )
1285            .await;
1286
1287            // Wait for another `Watch`.
1288            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1289                .await;
1290
1291            let events = read_uapi_events(&input_file, &current_task);
1292            assert_eq!(
1293                events,
1294                vec![
1295                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1296                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1297                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1298                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1299                ]
1300            );
1301        })
1302        .await;
1303    }
1304
1305    #[::fuchsia::test]
1306    async fn translates_touch_remove() {
1307        spawn_kernel_and_run(async move |current_task| {
1308            // Set up resources.
1309
1310            let (_input_device, input_file, mut touch_source_stream) =
1311                start_touch_input(&current_task).await;
1312
1313            // Touch add for pointer 1.
1314            answer_next_touch_watch_request(
1315                &mut touch_source_stream,
1316                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1317            )
1318            .await;
1319
1320            // Wait for another `Watch` to ensure input_file done processing the first reply.
1321            // Use an empty `TouchEvent`, to minimize the chance that this event
1322            // creates unexpected `uapi::input_event`s.
1323            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1324                .await;
1325
1326            // Consume all of the `uapi::input_event`s that are available.
1327            let events = read_uapi_events(&input_file, &current_task);
1328
1329            assert_eq!(events.len(), 6);
1330
1331            // Reply to touch change.
1332            answer_next_touch_watch_request(
1333                &mut touch_source_stream,
1334                vec![make_touch_event_with_phase(EventPhase::Remove, 1)],
1335            )
1336            .await;
1337
1338            // Wait for another `Watch`.
1339            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1340                .await;
1341
1342            let events = read_uapi_events(&input_file, &current_task);
1343            assert_eq!(
1344                events,
1345                vec![
1346                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1347                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1348                    make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1349                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1350                ]
1351            );
1352        })
1353        .await;
1354    }
1355
1356    #[::fuchsia::test]
1357    async fn multi_touch_event_sequence() {
1358        spawn_kernel_and_run(async move |current_task| {
1359            // Set up resources.
1360
1361            let (_input_device, input_file, mut touch_source_stream) =
1362                start_touch_input(&current_task).await;
1363
1364            // Touch add for pointer 1.
1365            answer_next_touch_watch_request(
1366                &mut touch_source_stream,
1367                vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1368            )
1369            .await;
1370
1371            // Wait for another `Watch`.
1372            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1373                .await;
1374            let events = read_uapi_events(&input_file, &current_task);
1375
1376            assert_eq!(events.len(), 6);
1377
1378            // Touch add for pointer 2.
1379            answer_next_touch_watch_request(
1380                &mut touch_source_stream,
1381                vec![
1382                    make_touch_event_with_coords(10.0, 20.0, 1),
1383                    make_touch_event_with_phase(EventPhase::Add, 2),
1384                ],
1385            )
1386            .await;
1387
1388            // Wait for another `Watch`.
1389            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1390                .await;
1391            let events = read_uapi_events(&input_file, &current_task);
1392
1393            assert_eq!(
1394                events,
1395                vec![
1396                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1397                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1398                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1399                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1400                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1401                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1402                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1403                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1404                ]
1405            );
1406
1407            // Both pointers move.
1408            answer_next_touch_watch_request(
1409                &mut touch_source_stream,
1410                vec![
1411                    make_touch_event_with_coords(11.0, 21.0, 1),
1412                    make_touch_event_with_coords(101.0, 201.0, 2),
1413                ],
1414            )
1415            .await;
1416
1417            // Wait for another `Watch`.
1418            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1419                .await;
1420            let events = read_uapi_events(&input_file, &current_task);
1421
1422            assert_eq!(
1423                events,
1424                vec![
1425                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1426                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
1427                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
1428                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1429                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
1430                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
1431                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1432                ]
1433            );
1434
1435            // Pointer 1 up.
1436            answer_next_touch_watch_request(
1437                &mut touch_source_stream,
1438                vec![
1439                    make_touch_event_with_phase(EventPhase::Remove, 1),
1440                    make_touch_event_with_coords(102.0, 202.0, 2),
1441                ],
1442            )
1443            .await;
1444
1445            // Wait for another `Watch`.
1446            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1447                .await;
1448            let events = read_uapi_events(&input_file, &current_task);
1449
1450            assert_eq!(
1451                events,
1452                vec![
1453                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1454                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1455                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1456                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 102),
1457                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 202),
1458                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1459                ]
1460            );
1461
1462            // Pointer 2 up.
1463            answer_next_touch_watch_request(
1464                &mut touch_source_stream,
1465                vec![make_touch_event_with_phase(EventPhase::Remove, 2)],
1466            )
1467            .await;
1468
1469            // Wait for another `Watch`.
1470            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1471                .await;
1472            let events = read_uapi_events(&input_file, &current_task);
1473
1474            assert_eq!(
1475                events,
1476                vec![
1477                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1478                    make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1479                    make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1480                    make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1481                ]
1482            );
1483        })
1484        .await;
1485    }
1486
1487    #[::fuchsia::test]
1488    async fn multi_event_sequence_unsorted_in_one_watch() {
1489        spawn_kernel_and_run(async move |current_task| {
1490            // Set up resources.
1491
1492            let (_input_device, input_file, mut touch_source_stream) =
1493                start_touch_input(&current_task).await;
1494
1495            // Touch add for pointer 1.
1496            answer_next_touch_watch_request(
1497                &mut touch_source_stream,
1498                vec![
1499                    make_touch_event_with_coords_phase_timestamp(
1500                        10.0,
1501                        20.0,
1502                        EventPhase::Change,
1503                        1,
1504                        100,
1505                    ),
1506                    make_touch_event_with_coords_phase_timestamp(0.0, 0.0, EventPhase::Add, 1, 1),
1507                ],
1508            )
1509            .await;
1510
1511            // Wait for another `Watch`.
1512            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1513                .await;
1514            let events = read_uapi_events(&input_file, &current_task);
1515
1516            assert_eq!(
1517                events,
1518                vec![
1519                    make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
1520                    make_uapi_input_event_with_timestamp(
1521                        uapi::EV_ABS,
1522                        uapi::ABS_MT_TRACKING_ID,
1523                        1,
1524                        1
1525                    ),
1526                    make_uapi_input_event_with_timestamp(
1527                        uapi::EV_ABS,
1528                        uapi::ABS_MT_POSITION_X,
1529                        0,
1530                        1
1531                    ),
1532                    make_uapi_input_event_with_timestamp(
1533                        uapi::EV_ABS,
1534                        uapi::ABS_MT_POSITION_Y,
1535                        0,
1536                        1
1537                    ),
1538                    make_uapi_input_event_with_timestamp(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
1539                    make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
1540                    make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 100),
1541                    make_uapi_input_event_with_timestamp(
1542                        uapi::EV_ABS,
1543                        uapi::ABS_MT_POSITION_X,
1544                        10,
1545                        100
1546                    ),
1547                    make_uapi_input_event_with_timestamp(
1548                        uapi::EV_ABS,
1549                        uapi::ABS_MT_POSITION_Y,
1550                        20,
1551                        100
1552                    ),
1553                    make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 100),
1554                ]
1555            );
1556        })
1557        .await;
1558    }
1559
1560    #[test_case((0.0, 0.0); "origin")]
1561    #[test_case((100.7, 200.7); "above midpoint")]
1562    #[test_case((100.3, 200.3); "below midpoint")]
1563    #[test_case((100.5, 200.5); "midpoint")]
1564    #[::fuchsia::test]
1565    async fn sends_acceptable_coordinates((x, y): (f32, f32)) {
1566        spawn_kernel_and_run(async move |current_task| {
1567            // Set up resources.
1568            let (_input_device, input_file, mut touch_source_stream) =
1569                start_touch_input(&current_task).await;
1570
1571            // Touch add.
1572            answer_next_touch_watch_request(
1573                &mut touch_source_stream,
1574                vec![make_touch_event_with_coords_phase(x, y, EventPhase::Add, 1)],
1575            )
1576            .await;
1577
1578            // Wait for another `Watch`.
1579            answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1580                .await;
1581            let events = read_uapi_events(&input_file, &current_task);
1582
1583            // Check that the reported positions are within the acceptable error. The acceptable
1584            // error is chosen to allow either rounding or truncation.
1585            const ACCEPTABLE_ERROR: f32 = 1.0;
1586            let actual_x = events
1587                .iter()
1588                .find(|event| {
1589                    event.type_ == uapi::EV_ABS as u16
1590                        && event.code == uapi::ABS_MT_POSITION_X as u16
1591                })
1592                .unwrap_or_else(|| panic!("did not find `ABS_X` event in {:?}", events))
1593                .value;
1594            let actual_y = events
1595                .iter()
1596                .find(|event| {
1597                    event.type_ == uapi::EV_ABS as u16
1598                        && event.code == uapi::ABS_MT_POSITION_Y as u16
1599                })
1600                .unwrap_or_else(|| panic!("did not find `ABS_Y` event in {:?}", events))
1601                .value;
1602            assert_near!(x, actual_x as f32, ACCEPTABLE_ERROR);
1603            assert_near!(y, actual_y as f32, ACCEPTABLE_ERROR);
1604        })
1605        .await;
1606    }
1607
1608    #[::fuchsia::test]
1609    async fn sends_appropriate_ack_to_touch_source_server() {
1610        spawn_kernel_and_run(async move |current_task| {
1611            let (_input_device, _input_file, mut touch_source_stream) =
1612                start_touch_input(&current_task).await;
1613
1614            let control_handle = touch_source_stream.control_handle();
1615            control_handle
1616                .send_on_touch_events(vec![make_touch_event_with_phase(EventPhase::Add, 2)], 42)
1617                .expect("failure sending OnTouchEvents");
1618            match touch_source_stream.next().await {
1619                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
1620                    last_acknowledged_event_stamp,
1621                    ..
1622                })) => assert_eq!(last_acknowledged_event_stamp, 42),
1623                unexpected => panic!("unexpected request {:?}", unexpected),
1624            }
1625        })
1626        .await;
1627    }
1628
1629    #[test_case(fidl_fuchsia_input::Key::Escape, uapi::KEY_POWER; "Esc maps to Power")]
1630    #[test_case(fidl_fuchsia_input::Key::A, uapi::KEY_A; "A maps to A")]
1631    #[::fuchsia::test]
1632    async fn sends_keyboard_events(fkey: fidl_fuchsia_input::Key, lkey: u32) {
1633        spawn_kernel_and_run(async move |current_task| {
1634            let (_keyboard_device, keyboard_file, keyboard_listener) =
1635                start_keyboard_input(&current_task).await;
1636
1637            let key_event = fuiinput::KeyEvent {
1638                timestamp: Some(0),
1639                type_: Some(fuiinput::KeyEventType::Pressed),
1640                key: Some(fkey),
1641                ..Default::default()
1642            };
1643
1644            let _ = keyboard_listener.on_key_event(&key_event).await;
1645            std::mem::drop(keyboard_listener); // Close Zircon channel.
1646            let events = read_uapi_events(&keyboard_file, &current_task);
1647            assert_eq!(events.len(), 2);
1648            assert_eq!(events[0].code, lkey as u16);
1649        })
1650        .await;
1651    }
1652
1653    #[::fuchsia::test]
1654    async fn skips_unknown_keyboard_events() {
1655        spawn_kernel_and_run(async move |current_task| {
1656            let inspector = fuchsia_inspect::Inspector::default();
1657            let (_keyboard_device, keyboard_file, keyboard_listener) =
1658                start_keyboard_input_inspect(&current_task, &inspector).await;
1659
1660            let key_event = fuiinput::KeyEvent {
1661                timestamp: Some(0),
1662                type_: Some(fuiinput::KeyEventType::Pressed),
1663                key: Some(fidl_fuchsia_input::Key::AcRefresh),
1664                ..Default::default()
1665            };
1666
1667            let _ = keyboard_listener.on_key_event(&key_event).await;
1668            std::mem::drop(keyboard_listener); // Close Zircon channel.
1669            let events = read_uapi_events(&keyboard_file, &current_task);
1670            assert_eq!(events.len(), 0);
1671
1672            // The unknown key is counted as received-but-ignored, and must not be
1673            // counted as converted.
1674            assert_data_tree!(inspector, root: {
1675                keyboard_device: {
1676                    active_wake_leases_count: AnyProperty,
1677                    total_events_with_wake_lease_count: AnyProperty,
1678                    total_fidl_events_received_count: 1u64,
1679                    total_fidl_events_ignored_count: 1u64,
1680                    total_fidl_events_unexpected_count: 0u64,
1681                    total_fidl_events_converted_count: 0u64,
1682                    total_uapi_events_generated_count: 0u64,
1683                    last_generated_uapi_event_timestamp_ns: 0i64,
1684                    keyboard_file_0: {
1685                        fidl_events_received_count: 1u64,
1686                        fidl_events_ignored_count: 1u64,
1687                        fidl_events_unexpected_count: 0u64,
1688                        fidl_events_converted_count: 0u64,
1689                        uapi_events_generated_count: 0u64,
1690                        uapi_events_read_count: 0u64,
1691                        fd_read_count: AnyProperty,
1692                        fd_notify_count: AnyProperty,
1693                        last_generated_uapi_event_timestamp_ns: 0i64,
1694                        last_read_uapi_event_timestamp_ns: AnyProperty,
1695                        opened_without_nonblock: AnyProperty,
1696                        open_timestamp_ns: AnyProperty,
1697                        closed: AnyProperty,
1698                        close_timestamp_ns: AnyProperty,
1699                    }
1700                }
1701            });
1702        })
1703        .await;
1704    }
1705
1706    #[::fuchsia::test]
1707    async fn sends_power_button_events() {
1708        spawn_kernel_and_run(async move |current_task| {
1709            let (_input_device, input_file, buttons_listener) =
1710                start_button_input(&current_task).await;
1711
1712            let power_event = MediaButtonsEvent {
1713                volume: Some(0),
1714                mic_mute: Some(false),
1715                pause: Some(false),
1716                camera_disable: Some(false),
1717                power: Some(true),
1718                function: Some(false),
1719                ..Default::default()
1720            };
1721
1722            let _ = buttons_listener.on_event(power_event).await;
1723            std::mem::drop(buttons_listener); // Close Zircon channel.
1724
1725            let events = read_uapi_events(&input_file, &current_task);
1726            assert_eq!(events.len(), 2);
1727            assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1728            assert_eq!(events[0].value, 1);
1729        })
1730        .await;
1731    }
1732
1733    #[::fuchsia::test]
1734    async fn sends_function_button_events() {
1735        spawn_kernel_and_run(async move |current_task| {
1736            let (_input_device, input_file, buttons_listener) =
1737                start_button_input(&current_task).await;
1738
1739            let function_event = MediaButtonsEvent {
1740                volume: Some(0),
1741                mic_mute: Some(false),
1742                pause: Some(false),
1743                camera_disable: Some(false),
1744                power: Some(false),
1745                function: Some(true),
1746                ..Default::default()
1747            };
1748
1749            let _ = buttons_listener.on_event(function_event).await;
1750            std::mem::drop(buttons_listener); // Close Zircon channel.
1751
1752            let events = read_uapi_events(&input_file, &current_task);
1753            assert_eq!(events.len(), 2);
1754            assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1755            assert_eq!(events[0].value, 1);
1756        })
1757        .await;
1758    }
1759
1760    #[::fuchsia::test]
1761    async fn sends_overlapping_button_events() {
1762        spawn_kernel_and_run(async move |current_task| {
1763            let (_input_device, input_file, buttons_listener) =
1764                start_button_input(&current_task).await;
1765
1766            let power_event = MediaButtonsEvent {
1767                volume: Some(0),
1768                mic_mute: Some(false),
1769                pause: Some(false),
1770                camera_disable: Some(false),
1771                power: Some(true),
1772                function: Some(false),
1773                ..Default::default()
1774            };
1775
1776            let function_event = MediaButtonsEvent {
1777                volume: Some(0),
1778                mic_mute: Some(false),
1779                pause: Some(false),
1780                camera_disable: Some(false),
1781                power: Some(true),
1782                function: Some(true),
1783                ..Default::default()
1784            };
1785
1786            let function_release_event = MediaButtonsEvent {
1787                volume: Some(0),
1788                mic_mute: Some(false),
1789                pause: Some(false),
1790                camera_disable: Some(false),
1791                power: Some(true),
1792                function: Some(false),
1793                ..Default::default()
1794            };
1795
1796            let power_release_event = MediaButtonsEvent {
1797                volume: Some(0),
1798                mic_mute: Some(false),
1799                pause: Some(false),
1800                camera_disable: Some(false),
1801                power: Some(false),
1802                function: Some(false),
1803                ..Default::default()
1804            };
1805
1806            let _ = buttons_listener.on_event(power_event).await;
1807            let _ = buttons_listener.on_event(function_event).await;
1808            let _ = buttons_listener.on_event(function_release_event).await;
1809            let _ = buttons_listener.on_event(power_release_event).await;
1810            std::mem::drop(buttons_listener); // Close Zircon channel.
1811
1812            let events = read_uapi_events(&input_file, &current_task);
1813            assert_eq!(events.len(), 8);
1814            assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1815            assert_eq!(events[0].value, 1);
1816            assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1817            assert_eq!(events[2].value, 1);
1818            assert_eq!(events[4].code, uapi::KEY_VOLUMEDOWN as u16);
1819            assert_eq!(events[4].value, 0);
1820            assert_eq!(events[6].code, uapi::KEY_POWER as u16);
1821            assert_eq!(events[6].value, 0);
1822        })
1823        .await;
1824    }
1825
1826    #[::fuchsia::test]
1827    async fn sends_simultaneous_button_events() {
1828        spawn_kernel_and_run(async move |current_task| {
1829            let (_input_device, input_file, buttons_listener) =
1830                start_button_input(&current_task).await;
1831
1832            let power_and_function_event = MediaButtonsEvent {
1833                volume: Some(0),
1834                mic_mute: Some(false),
1835                pause: Some(false),
1836                camera_disable: Some(false),
1837                power: Some(true),
1838                function: Some(true),
1839                ..Default::default()
1840            };
1841
1842            let _ = buttons_listener.on_event(power_and_function_event).await;
1843            std::mem::drop(buttons_listener); // Close Zircon channel.
1844
1845            let events = read_uapi_events(&input_file, &current_task);
1846            assert_eq!(events.len(), 4);
1847            assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1848            assert_eq!(events[0].value, 1);
1849            assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1850            assert_eq!(events[2].value, 1);
1851        })
1852        .await;
1853    }
1854
1855    #[::fuchsia::test]
1856    async fn sends_volume_up_button_events() {
1857        spawn_kernel_and_run(async move |current_task| {
1858            let (_input_device, input_file, buttons_listener) =
1859                start_button_input(&current_task).await;
1860
1861            let volume_up_event = MediaButtonsEvent {
1862                volume: Some(1),
1863                mic_mute: Some(false),
1864                pause: Some(false),
1865                camera_disable: Some(false),
1866                power: Some(false),
1867                function: Some(false),
1868                ..Default::default()
1869            };
1870
1871            let _ = buttons_listener.on_event(volume_up_event).await;
1872            std::mem::drop(buttons_listener); // Close Zircon channel.
1873
1874            let events = read_uapi_events(&input_file, &current_task);
1875            assert_eq!(events.len(), 2);
1876            assert_eq!(events[0].code, uapi::KEY_VOLUMEUP as u16);
1877            assert_eq!(events[0].value, 1);
1878        })
1879        .await;
1880    }
1881
1882    #[::fuchsia::test]
1883    async fn sends_volume_down_button_events() {
1884        spawn_kernel_and_run(async move |current_task| {
1885            let (_input_device, input_file, buttons_listener) =
1886                start_button_input(&current_task).await;
1887
1888            let volume_down_event = MediaButtonsEvent {
1889                volume: Some(-1),
1890                mic_mute: Some(false),
1891                pause: Some(false),
1892                camera_disable: Some(false),
1893                power: Some(false),
1894                function: Some(false),
1895                ..Default::default()
1896            };
1897
1898            let _ = buttons_listener.on_event(volume_down_event).await;
1899            std::mem::drop(buttons_listener); // Close Zircon channel.
1900
1901            let events = read_uapi_events(&input_file, &current_task);
1902            assert_eq!(events.len(), 2);
1903            assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1904            assert_eq!(events[0].value, 1);
1905        })
1906        .await;
1907    }
1908
1909    #[test_case(1; "Scroll up")]
1910    #[test_case(-1; "Scroll down")]
1911    #[::fuchsia::test]
1912    async fn sends_mouse_wheel_events(ticks: i64) {
1913        spawn_kernel_and_run(async move |current_task| {
1914            let time = 100;
1915            let uapi_event = uapi::input_event {
1916                time: timeval_from_time(zx::MonotonicInstant::from_nanos(time)),
1917                type_: uapi::EV_REL as u16,
1918                code: uapi::REL_WHEEL as u16,
1919                value: ticks as i32,
1920            };
1921
1922            let (_mouse_device, mouse_file, mut mouse_stream) =
1923                start_mouse_input(&current_task).await;
1924
1925            answer_next_mouse_watch_request(
1926                &mut mouse_stream,
1927                vec![make_mouse_wheel_event_with_timestamp(ticks, time)],
1928            )
1929            .await;
1930
1931            // Wait for another `Watch` to ensure mouse_file is done processing the other replies.
1932            // Use an empty vec, to ensure no unexpected `uapi::input_event`s are created.
1933            answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1934
1935            let events = read_uapi_events(&mouse_file, &current_task);
1936            assert_eq!(events.len(), 2);
1937            assert_eq!(events[0], uapi_event);
1938        })
1939        .await;
1940    }
1941
1942    #[::fuchsia::test]
1943    async fn handles_mouse_move_and_click_events() {
1944        spawn_kernel_and_run(async move |current_task| {
1945            let mouse_move_event = fuipointer::MouseEvent {
1946                timestamp: Some(0),
1947                pointer_sample: Some(fuipointer::MousePointerSample {
1948                    device_id: Some(0),
1949                    relative_motion: Some([10.0, 20.0]),
1950                    ..Default::default()
1951                }),
1952                ..Default::default()
1953            };
1954            let mouse_click_event = fuipointer::MouseEvent {
1955                timestamp: Some(0),
1956                pointer_sample: Some(fuipointer::MousePointerSample {
1957                    device_id: Some(0),
1958                    pressed_buttons: Some(vec![1]),
1959                    ..Default::default()
1960                }),
1961                ..Default::default()
1962            };
1963
1964            let (_mouse_device, mouse_file, mut mouse_stream) =
1965                start_mouse_input(&current_task).await;
1966
1967            // Expect mouse relay to handle MouseEvents with movement and buttons.
1968            answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_move_event]).await;
1969            answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_click_event]).await;
1970
1971            // Wait for another `Watch` to ensure mouse_file is done processing the other replies.
1972            // Use an empty vec, to ensure no unexpected `uapi::input_event`s are created.
1973            answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
1974
1975            let events = read_uapi_events(&mouse_file, &current_task);
1976            assert!(!events.is_empty());
1977            assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
1978                && e.code == uapi::REL_X as u16
1979                && e.value == 10));
1980            assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
1981                && e.code == uapi::REL_Y as u16
1982                && e.value == 20));
1983            assert!(events.iter().any(|e| e.type_ == uapi::EV_KEY as u16
1984                && e.code == uapi::BTN_LEFT as u16
1985                && e.value == 1));
1986        })
1987        .await;
1988    }
1989
1990    #[::fuchsia::test]
1991    async fn touch_input_initialized_with_inspect_node() {
1992        spawn_kernel_and_run(async move |current_task| {
1993            let inspector = fuchsia_inspect::Inspector::default();
1994            let touch_device = InputDevice::new_touch(
1995                1200, /* screen width */
1996                720,  /* screen height */
1997                &inspector.root(),
1998            );
1999            let _file_obj = touch_device.open_test(&current_task);
2000
2001            assert_data_tree!(inspector, root: {
2002                touch_device: {
2003                    active_wake_leases_count: 0u64,
2004                    total_events_with_wake_lease_count: 0u64,
2005                    total_fidl_events_received_count: 0u64,
2006                    total_fidl_events_ignored_count: 0u64,
2007                    total_fidl_events_unexpected_count: 0u64,
2008                    total_fidl_events_converted_count: 0u64,
2009                    total_uapi_events_generated_count: 0u64,
2010                    last_generated_uapi_event_timestamp_ns: 0i64,
2011                    touch_file_0: {
2012                        fidl_events_received_count: 0u64,
2013                        fidl_events_ignored_count: 0u64,
2014                        fidl_events_unexpected_count: 0u64,
2015                        fidl_events_converted_count: 0u64,
2016                        uapi_events_generated_count: 0u64,
2017                        uapi_events_read_count: 0u64,
2018                        fd_read_count: 0u64,
2019                        fd_notify_count: 0u64,
2020                        last_generated_uapi_event_timestamp_ns: 0i64,
2021                        last_read_uapi_event_timestamp_ns: 0i64,
2022                        opened_without_nonblock: AnyProperty,
2023                        open_timestamp_ns: AnyProperty,
2024                        closed: AnyProperty,
2025                        close_timestamp_ns: AnyProperty,
2026                    }
2027                }
2028            });
2029        })
2030        .await;
2031    }
2032
2033    #[::fuchsia::test]
2034    async fn touch_relay_updates_touch_inspect_status() {
2035        spawn_kernel_and_run(async move |current_task| {
2036            let inspector = fuchsia_inspect::Inspector::default();
2037
2038            let (_input_device, input_file, mut touch_source_stream) =
2039                start_touch_input_inspect(&current_task, &inspector).await;
2040
2041            // Send 2 TouchEvents to proxy that should be counted as `received` by InputFile
2042            // A TouchEvent::default() has no pointer sample so these events should be discarded.
2043            let control_handle = touch_source_stream.control_handle();
2044            control_handle
2045                .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2046                .expect("failure sending OnTouchEvents");
2047            match touch_source_stream.next().await {
2048                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2049                    last_acknowledged_event_stamp,
2050                    ..
2051                })) => assert_eq!(last_acknowledged_event_stamp, 2),
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            control_handle
2058                .send_on_touch_events(
2059                    vec![
2060                        make_touch_event_with_coords_phase_timestamp(
2061                            0.0,
2062                            0.0,
2063                            EventPhase::Add,
2064                            1,
2065                            1000,
2066                        ),
2067                        make_touch_event_with_coords_phase_timestamp(
2068                            1.0,
2069                            1.0,
2070                            EventPhase::Change,
2071                            1,
2072                            2000,
2073                        ),
2074                        make_touch_event_with_coords_phase_timestamp(
2075                            2.0,
2076                            2.0,
2077                            EventPhase::Change,
2078                            1,
2079                            3000,
2080                        ),
2081                        make_touch_event_with_coords_phase_timestamp(
2082                            3.0,
2083                            3.0,
2084                            EventPhase::Change,
2085                            1,
2086                            4000,
2087                        ),
2088                        make_touch_event_with_coords_phase_timestamp(
2089                            3.0,
2090                            3.0,
2091                            EventPhase::Remove,
2092                            1,
2093                            5000,
2094                        ),
2095                    ],
2096                    7,
2097                )
2098                .expect("failure sending OnTouchEvents");
2099            match touch_source_stream.next().await {
2100                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2101                    last_acknowledged_event_stamp,
2102                    ..
2103                })) => assert_eq!(last_acknowledged_event_stamp, 7),
2104                unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2105            }
2106
2107            let _events = read_uapi_events(&input_file, &current_task);
2108            assert_data_tree!(inspector, root: {
2109                touch_device: {
2110                    active_wake_leases_count: 0u64,
2111                    total_events_with_wake_lease_count: 0u64,
2112                    total_fidl_events_received_count: 7u64,
2113                    total_fidl_events_ignored_count: 2u64,
2114                    total_fidl_events_unexpected_count: 0u64,
2115                    total_fidl_events_converted_count: 5u64,
2116                    total_uapi_events_generated_count: 22u64,
2117                    last_generated_uapi_event_timestamp_ns: 5000i64,
2118                    touch_file_0: {
2119                        fidl_events_received_count: 7u64,
2120                        fidl_events_ignored_count: 2u64,
2121                        fidl_events_unexpected_count: 0u64,
2122                        fidl_events_converted_count: 5u64,
2123                        uapi_events_generated_count: 22u64,
2124                        uapi_events_read_count: 22u64,
2125                        fd_read_count: 23u64,
2126                        fd_notify_count: 1u64,
2127                        last_generated_uapi_event_timestamp_ns: 5000i64,
2128                        last_read_uapi_event_timestamp_ns: 5000i64,
2129                        opened_without_nonblock: AnyProperty,
2130                        open_timestamp_ns: AnyProperty,
2131                        closed: AnyProperty,
2132                        close_timestamp_ns: AnyProperty,
2133                    },
2134                }
2135            });
2136        })
2137        .await;
2138    }
2139
2140    #[::fuchsia::test]
2141    async fn new_file_updates_inspect_status() {
2142        spawn_kernel_and_run(async move |current_task| {
2143            let inspector = fuchsia_inspect::Inspector::default();
2144
2145            let input_device = InputDevice::new_touch(700, 700, inspector.root());
2146            let input_file_0 =
2147                input_device.open_test(&current_task).expect("Failed to create input file");
2148
2149            let (touch_source_client_end, mut touch_source_stream) =
2150                fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
2151            let (mouse_source_client_end, _mouse_source_stream) =
2152                fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
2153            let (keyboard_proxy, mut keyboard_stream) =
2154                fidl::endpoints::create_sync_proxy_and_stream::<fuiinput::KeyboardMarker>();
2155            let view_ref_pair =
2156                fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2157            let (device_registry_proxy, mut device_listener_stream) =
2158                fidl::endpoints::create_sync_proxy_and_stream::<
2159                    fuipolicy::DeviceListenerRegistryMarker,
2160                >();
2161
2162            let (relay, relay_handle) = input_event_relay::new_input_relay();
2163            relay.start_relays(
2164                &current_task.kernel(),
2165                EventProxyMode::None,
2166                touch_source_client_end,
2167                keyboard_proxy,
2168                mouse_source_client_end,
2169                view_ref_pair.view_ref,
2170                device_registry_proxy,
2171                input_device.open_files.clone(),
2172                Default::default(),
2173                None,
2174                Some(input_device.inspect_status.clone()),
2175                None,
2176            );
2177
2178            let _ = init_keyboard_listener(&mut keyboard_stream).await;
2179            let _ = init_button_listeners(&mut device_listener_stream).await;
2180
2181            relay_handle.add_touch_device(
2182                0,
2183                input_device.open_files.clone(),
2184                Some(input_device.inspect_status.clone()),
2185            );
2186
2187            // Send 2 TouchEvents to proxy that should be counted as `received` by InputFile
2188            // A TouchEvent::default() has no pointer sample so these events should be discarded.
2189            let control_handle = touch_source_stream.control_handle();
2190            control_handle
2191                .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2192                .expect("failure sending OnTouchEvents");
2193            match touch_source_stream.next().await {
2194                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2195                    last_acknowledged_event_stamp,
2196                    ..
2197                })) => assert_eq!(last_acknowledged_event_stamp, 2),
2198                unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2199            }
2200
2201            // Verify file node & properties remain in inspect tree when file is closed
2202            input_device.open_files.lock().clear();
2203            drop(input_file_0);
2204
2205            // Open new file which should receive input_device's subsequent events
2206            let input_file_1 =
2207                input_device.open_test(&current_task).expect("Failed to create input file");
2208
2209            // Send 5 TouchEvents with pointer sample to proxy, these should be received and converted
2210            // Add/Remove events generate 5 uapi events each. Change events generate 3 uapi events each.
2211            control_handle
2212                .send_on_touch_events(
2213                    vec![
2214                        make_touch_event_with_coords_phase_timestamp(
2215                            0.0,
2216                            0.0,
2217                            EventPhase::Add,
2218                            1,
2219                            1000,
2220                        ),
2221                        make_touch_event_with_coords_phase_timestamp(
2222                            1.0,
2223                            1.0,
2224                            EventPhase::Change,
2225                            1,
2226                            2000,
2227                        ),
2228                        make_touch_event_with_coords_phase_timestamp(
2229                            2.0,
2230                            2.0,
2231                            EventPhase::Change,
2232                            1,
2233                            3000,
2234                        ),
2235                        make_touch_event_with_coords_phase_timestamp(
2236                            3.0,
2237                            3.0,
2238                            EventPhase::Change,
2239                            1,
2240                            4000,
2241                        ),
2242                        make_touch_event_with_coords_phase_timestamp(
2243                            3.0,
2244                            3.0,
2245                            EventPhase::Remove,
2246                            1,
2247                            5000,
2248                        ),
2249                    ],
2250                    7,
2251                )
2252                .expect("failure sending OnTouchEvents");
2253            match touch_source_stream.next().await {
2254                Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2255                    last_acknowledged_event_stamp,
2256                    ..
2257                })) => assert_eq!(last_acknowledged_event_stamp, 7),
2258                unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2259            }
2260
2261            let _events = read_uapi_events(&input_file_1, &current_task);
2262
2263            // Verify file node & properties remain in inspect tree when file is closed
2264            input_device.open_files.lock().clear();
2265            drop(input_file_1);
2266
2267            assert_data_tree!(inspector, root: {
2268                touch_device: {
2269                    active_wake_leases_count: 0u64,
2270                    total_events_with_wake_lease_count: 0u64,
2271                    total_fidl_events_received_count: 7u64,
2272                    total_fidl_events_ignored_count: 2u64,
2273                    total_fidl_events_unexpected_count: 0u64,
2274                    total_fidl_events_converted_count: 5u64,
2275                    total_uapi_events_generated_count: 22u64,
2276                    last_generated_uapi_event_timestamp_ns: 5000i64,
2277                    touch_file_0: {
2278                        fidl_events_received_count: 2u64,
2279                        fidl_events_ignored_count: 2u64,
2280                        fidl_events_unexpected_count: 0u64,
2281                        fidl_events_converted_count: 0u64,
2282                        uapi_events_generated_count: 0u64,
2283                        uapi_events_read_count: 0u64,
2284                        fd_read_count: 0u64,
2285                        fd_notify_count: 0u64,
2286                        last_generated_uapi_event_timestamp_ns: 0i64,
2287                        last_read_uapi_event_timestamp_ns: 0i64,
2288                        opened_without_nonblock: AnyProperty,
2289                        open_timestamp_ns: AnyProperty,
2290                        closed: AnyProperty,
2291                        close_timestamp_ns: AnyProperty,
2292                    },
2293                    touch_file_1: {
2294                        fidl_events_received_count: 5u64,
2295                        fidl_events_ignored_count: 0u64,
2296                        fidl_events_unexpected_count: 0u64,
2297                        fidl_events_converted_count: 5u64,
2298                        uapi_events_generated_count: 22u64,
2299                        uapi_events_read_count: 22u64,
2300                        fd_read_count: 23u64,
2301                        fd_notify_count: 1u64,
2302                        last_generated_uapi_event_timestamp_ns: 5000i64,
2303                        last_read_uapi_event_timestamp_ns: 5000i64,
2304                        opened_without_nonblock: AnyProperty,
2305                        open_timestamp_ns: AnyProperty,
2306                        closed: AnyProperty,
2307                        close_timestamp_ns: AnyProperty,
2308                    },
2309                }
2310            });
2311        })
2312        .await;
2313    }
2314
2315    #[::fuchsia::test]
2316    async fn file_status_inspect_not_empty_after_close() {
2317        spawn_kernel_and_run(async move |current_task| {
2318            let inspector = fuchsia_inspect::Inspector::default();
2319
2320            let input_device = InputDevice::new_touch(700, 700, inspector.root());
2321            let file_handle =
2322                input_device.open_test(&current_task).expect("Failed to create input file");
2323
2324            let status = file_handle
2325                .downcast_file::<crate::input_file::ArcInputFile>()
2326                .unwrap()
2327                .0
2328                .inspect_status
2329                .clone()
2330                .expect("touch file must have status");
2331            status.count_received_events(5);
2332
2333            assert_data_tree!(inspector, root: {
2334                touch_device: {
2335                    active_wake_leases_count: AnyProperty,
2336                    last_generated_uapi_event_timestamp_ns: AnyProperty,
2337                    total_events_with_wake_lease_count: AnyProperty,
2338                    total_fidl_events_converted_count: AnyProperty,
2339                    total_fidl_events_ignored_count: AnyProperty,
2340                    total_fidl_events_received_count: AnyProperty,
2341                    total_fidl_events_unexpected_count: AnyProperty,
2342                    total_uapi_events_generated_count: AnyProperty,
2343                    touch_file_0: {
2344                        fidl_events_received_count: 5u64,
2345                        fd_notify_count: 0u64,
2346                        fd_read_count: 0u64,
2347                        fidl_events_converted_count: 0u64,
2348                        fidl_events_ignored_count: 0u64,
2349                        fidl_events_unexpected_count: 0u64,
2350                        last_generated_uapi_event_timestamp_ns: 0i64,
2351                        last_read_uapi_event_timestamp_ns: 0i64,
2352                        uapi_events_generated_count: 0u64,
2353                        uapi_events_read_count: 0u64,
2354                        opened_without_nonblock: true,
2355                        open_timestamp_ns: AnyProperty,
2356                        closed: false,
2357                        close_timestamp_ns: 0i64,
2358                    }
2359                }
2360            });
2361
2362            drop(status);
2363            input_device.open_files.lock().clear();
2364            drop(file_handle);
2365
2366            assert_data_tree!(inspector, root: {
2367                touch_device: {
2368                    active_wake_leases_count: AnyProperty,
2369                    last_generated_uapi_event_timestamp_ns: AnyProperty,
2370                    total_events_with_wake_lease_count: AnyProperty,
2371                    total_fidl_events_converted_count: AnyProperty,
2372                    total_fidl_events_ignored_count: AnyProperty,
2373                    total_fidl_events_received_count: AnyProperty,
2374                    total_fidl_events_unexpected_count: AnyProperty,
2375                    total_uapi_events_generated_count: AnyProperty,
2376                    touch_file_0: {
2377                        fidl_events_received_count: 5u64,
2378                        fd_notify_count: 0u64,
2379                        fd_read_count: 0u64,
2380                        fidl_events_converted_count: 0u64,
2381                        fidl_events_ignored_count: 0u64,
2382                        fidl_events_unexpected_count: 0u64,
2383                        last_generated_uapi_event_timestamp_ns: 0i64,
2384                        last_read_uapi_event_timestamp_ns: 0i64,
2385                        uapi_events_generated_count: 0u64,
2386                        uapi_events_read_count: 0u64,
2387                        opened_without_nonblock: true,
2388                        open_timestamp_ns: AnyProperty,
2389                        closed: AnyProperty,
2390                        close_timestamp_ns: AnyProperty,
2391                    }
2392                }
2393            });
2394        })
2395        .await;
2396    }
2397
2398    #[::fuchsia::test]
2399    async fn keyboard_input_initialized_with_inspect_node() {
2400        spawn_kernel_and_run(async move |current_task| {
2401            let inspector = fuchsia_inspect::Inspector::default();
2402            let keyboard_device = InputDevice::new_keyboard(&inspector.root());
2403            let _file_obj = keyboard_device.open_test(&current_task);
2404
2405            assert_data_tree!(inspector, root: {
2406                keyboard_device: {
2407                    active_wake_leases_count: 0u64,
2408                    total_events_with_wake_lease_count: 0u64,
2409                    total_fidl_events_received_count: 0u64,
2410                    total_fidl_events_ignored_count: 0u64,
2411                    total_fidl_events_unexpected_count: 0u64,
2412                    total_fidl_events_converted_count: 0u64,
2413                    total_uapi_events_generated_count: 0u64,
2414                    last_generated_uapi_event_timestamp_ns: 0i64,
2415                    keyboard_file_0: {
2416                        fidl_events_received_count: 0u64,
2417                        fidl_events_ignored_count: 0u64,
2418                        fidl_events_unexpected_count: 0u64,
2419                        fidl_events_converted_count: 0u64,
2420                        uapi_events_generated_count: 0u64,
2421                        uapi_events_read_count: 0u64,
2422                        fd_read_count: 0u64,
2423                        fd_notify_count: 0u64,
2424                        last_generated_uapi_event_timestamp_ns: 0i64,
2425                        last_read_uapi_event_timestamp_ns: 0i64,
2426                        opened_without_nonblock: AnyProperty,
2427                        open_timestamp_ns: AnyProperty,
2428                        closed: AnyProperty,
2429                        close_timestamp_ns: AnyProperty,
2430                    }
2431                }
2432            });
2433        })
2434        .await;
2435    }
2436
2437    #[::fuchsia::test]
2438    async fn button_relay_updates_keyboard_inspect_status() {
2439        spawn_kernel_and_run(async move |current_task| {
2440            let inspector = fuchsia_inspect::Inspector::default();
2441
2442            let (_input_device, input_file, buttons_listener) =
2443                start_button_input_inspect(&current_task, &inspector).await;
2444
2445            // Each of these events should count toward received and converted.
2446            // They also generate 2 uapi events each.
2447            let power_event = MediaButtonsEvent {
2448                volume: Some(0),
2449                mic_mute: Some(false),
2450                pause: Some(false),
2451                camera_disable: Some(false),
2452                power: Some(true),
2453                function: Some(false),
2454                ..Default::default()
2455            };
2456
2457            let power_release_event = MediaButtonsEvent {
2458                volume: Some(0),
2459                mic_mute: Some(false),
2460                pause: Some(false),
2461                camera_disable: Some(false),
2462                power: Some(false),
2463                function: Some(false),
2464                ..Default::default()
2465            };
2466
2467            let _ = buttons_listener.on_event(power_event).await;
2468            let _ = buttons_listener.on_event(power_release_event).await;
2469
2470            let events = read_uapi_events(&input_file, &current_task);
2471            assert_eq!(events.len(), 4);
2472            assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2473            assert_eq!(events[0].value, 1);
2474            assert_eq!(events[2].code, uapi::KEY_POWER as u16);
2475            assert_eq!(events[2].value, 0);
2476
2477            let _events = read_uapi_events(&input_file, &current_task);
2478
2479            assert_data_tree!(inspector, root: {
2480                keyboard_device: {
2481                    active_wake_leases_count: 0u64,
2482                    total_events_with_wake_lease_count: 0u64,
2483                    total_fidl_events_received_count: 2u64,
2484                    total_fidl_events_ignored_count: 0u64,
2485                    total_fidl_events_unexpected_count: 0u64,
2486                    total_fidl_events_converted_count: 2u64,
2487                    total_uapi_events_generated_count: 4u64,
2488                    // Button events perform a realtime clockread, so any value will do.
2489                    last_generated_uapi_event_timestamp_ns: AnyProperty,
2490                    keyboard_file_0: {
2491                        fidl_events_received_count: 2u64,
2492                        fidl_events_ignored_count: 0u64,
2493                        fidl_events_unexpected_count: 0u64,
2494                        fidl_events_converted_count: 2u64,
2495                        uapi_events_generated_count: 4u64,
2496                        uapi_events_read_count: 4u64,
2497                        fd_read_count: 6u64,
2498                        fd_notify_count: 2u64,
2499                        // Button events perform a realtime clockread, so any value will do.
2500                        last_generated_uapi_event_timestamp_ns: AnyProperty,
2501                        last_read_uapi_event_timestamp_ns: AnyProperty,
2502                        opened_without_nonblock: AnyProperty,
2503                        open_timestamp_ns: AnyProperty,
2504                        closed: AnyProperty,
2505                        close_timestamp_ns: AnyProperty,
2506                    },
2507                }
2508            });
2509        })
2510        .await;
2511    }
2512
2513    #[::fuchsia::test]
2514    async fn mouse_input_initialized_with_inspect_node() {
2515        spawn_kernel_and_run(async move |current_task| {
2516            let inspector = fuchsia_inspect::Inspector::default();
2517            let mouse_device = InputDevice::new_mouse(&inspector.root());
2518            let _file_obj = mouse_device.open_test(&current_task);
2519
2520            assert_data_tree!(inspector, root: {
2521                mouse_device: {
2522                    active_wake_leases_count: 0u64,
2523                    total_events_with_wake_lease_count: 0u64,
2524                    total_fidl_events_received_count: 0u64,
2525                    total_fidl_events_ignored_count: 0u64,
2526                    total_fidl_events_unexpected_count: 0u64,
2527                    total_fidl_events_converted_count: 0u64,
2528                    total_uapi_events_generated_count: 0u64,
2529                    last_generated_uapi_event_timestamp_ns: 0i64,
2530                    mouse_file_0: {
2531                        fidl_events_received_count: 0u64,
2532                        fidl_events_ignored_count: 0u64,
2533                        fidl_events_unexpected_count: 0u64,
2534                        fidl_events_converted_count: 0u64,
2535                        uapi_events_generated_count: 0u64,
2536                        uapi_events_read_count: 0u64,
2537                        fd_read_count: 0u64,
2538                        fd_notify_count: 0u64,
2539                        last_generated_uapi_event_timestamp_ns: 0i64,
2540                        last_read_uapi_event_timestamp_ns: 0i64,
2541                        opened_without_nonblock: AnyProperty,
2542                        open_timestamp_ns: AnyProperty,
2543                        closed: AnyProperty,
2544                        close_timestamp_ns: AnyProperty,
2545                    }
2546                }
2547            });
2548        })
2549        .await;
2550    }
2551
2552    #[::fuchsia::test]
2553    async fn mouse_relay_updates_mouse_inspect_status() {
2554        spawn_kernel_and_run(async move |current_task| {
2555            let inspector = fuchsia_inspect::Inspector::default();
2556
2557            let (_input_device, input_file, mut mouse_source_stream) =
2558                start_mouse_input_inspect(&current_task, &inspector).await;
2559
2560            let mouse_move_event = fuipointer::MouseEvent {
2561                timestamp: Some(0),
2562                pointer_sample: Some(fuipointer::MousePointerSample {
2563                    device_id: Some(0),
2564                    position_in_viewport: Some([50.0, 50.0]),
2565                    scroll_v: Some(0),
2566                    ..Default::default()
2567                }),
2568                ..Default::default()
2569            };
2570            let mouse_click_event = fuipointer::MouseEvent {
2571                timestamp: Some(0),
2572                pointer_sample: Some(fuipointer::MousePointerSample {
2573                    device_id: Some(0),
2574                    scroll_v: Some(0),
2575                    pressed_buttons: Some(vec![1]),
2576                    ..Default::default()
2577                }),
2578                ..Default::default()
2579            };
2580
2581            // Send 2 MouseEvents: one move event with no relative motion (ignored) and one click
2582            // event with button 1 pressed (converted).
2583            answer_next_mouse_watch_request(
2584                &mut mouse_source_stream,
2585                vec![mouse_move_event, mouse_click_event],
2586            )
2587            .await;
2588
2589            // Send 5 MouseEvents with non-zero scroll_v delta to proxy, these should be received and
2590            // converted to 1 uapi event each, with an extra sync event to signify end of the batch.
2591            answer_next_mouse_watch_request(
2592                &mut mouse_source_stream,
2593                (0..5).map(|_| make_mouse_wheel_event(1)).collect(),
2594            )
2595            .await;
2596
2597            // Send a final mouse wheel event and ensure the inspect tree reflects it's timestamp under
2598            // last_generated_uapi_event_timestamp_ns and last_read_uapi_event_timestamp_ns.
2599            answer_next_mouse_watch_request(
2600                &mut mouse_source_stream,
2601                vec![make_mouse_wheel_event_with_timestamp(-1, 5000)],
2602            )
2603            .await;
2604
2605            // Wait for another `Watch` to ensure mouse_file is done processing the other replies.
2606            // Use an empty vec, to ensure no unexpected `uapi::input_event`s are created.
2607            answer_next_mouse_watch_request(&mut mouse_source_stream, vec![]).await;
2608
2609            let _events = read_uapi_events(&input_file, &current_task);
2610            assert_data_tree!(inspector, root: {
2611                mouse_device: {
2612                    active_wake_leases_count: 0u64,
2613                    total_events_with_wake_lease_count: 0u64,
2614                    total_fidl_events_received_count: 8u64,
2615                    total_fidl_events_ignored_count: 1u64,
2616                    total_fidl_events_unexpected_count: 0u64,
2617                    total_fidl_events_converted_count: 7u64,
2618                    total_uapi_events_generated_count: 7u64,
2619                    last_generated_uapi_event_timestamp_ns: 5000i64,
2620                    mouse_file_0: {
2621                        fidl_events_received_count: 8u64,
2622                        fidl_events_ignored_count: 1u64,
2623                        fidl_events_unexpected_count: 0u64,
2624                        fidl_events_converted_count: 7u64,
2625                        uapi_events_generated_count: 7u64,
2626                        uapi_events_read_count: 7u64,
2627                        fd_read_count: 8u64,
2628                        fd_notify_count: 3u64,
2629                        last_generated_uapi_event_timestamp_ns: 5000i64,
2630                        last_read_uapi_event_timestamp_ns: 5000i64,
2631                        opened_without_nonblock: AnyProperty,
2632                        open_timestamp_ns: AnyProperty,
2633                        closed: AnyProperty,
2634                        close_timestamp_ns: AnyProperty,
2635                    },
2636                }
2637            });
2638        })
2639        .await;
2640    }
2641
2642    #[::fuchsia::test]
2643    async fn eviocgname_zero_buffer_length_succeeds() {
2644        spawn_kernel_and_run(async move |current_task| {
2645            let inspector = fuchsia_inspect::Inspector::default();
2646            let touch_device = InputDevice::new_touch(700, 1200, &inspector.root());
2647            let file =
2648                touch_device.open_test(&current_task).expect("Failed to open touch input file");
2649
2650            let user_addr = starnix_core::testing::map_memory(
2651                &current_task,
2652                starnix_uapi::user_address::UserAddress::default(),
2653                4096,
2654            );
2655
2656            // EVIOCGNAME with 0 buffer length should return SUCCESS and not panic or write memory.
2657            let res = file.ioctl(&current_task, uapi::EVIOCGNAME_0, user_addr.into());
2658            assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2659
2660            // EVIOCGNAME with non-zero buffer length should succeed and return bytes written.
2661            let name_req = uapi::EVIOCGNAME_0 | (256 << 16);
2662            let res = file.ioctl(&current_task, name_req, user_addr.into());
2663            assert!(res.is_ok());
2664            let bytes_written = res.unwrap().value() as usize;
2665            assert!(bytes_written > 0);
2666        })
2667        .await;
2668    }
2669
2670    #[::fuchsia::test]
2671    async fn evdev_variable_length_ioctls_succeed() {
2672        spawn_kernel_and_run(async move |current_task| {
2673            let inspector = fuchsia_inspect::Inspector::default();
2674            let touch_device = InputDevice::new_touch(700, 1200, &inspector.root());
2675            let file =
2676                touch_device.open_test(&current_task).expect("Failed to open touch input file");
2677
2678            let user_addr = starnix_core::testing::map_memory(
2679                &current_task,
2680                starnix_uapi::user_address::UserAddress::default(),
2681                4096,
2682            );
2683
2684            // Test EVIOCGRAB succeeds.
2685            let res = file.ioctl(&current_task, uapi::EVIOCGRAB, 1u64.into());
2686            assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2687
2688            // Test EVIOCGPHYS with variable length.
2689            let phys_req = crate::input_file::EVIOCGPHYS_BASE | (256 << 16);
2690            let res = file.ioctl(&current_task, phys_req, user_addr.into());
2691            assert!(res.is_ok());
2692            let bytes_written = res.unwrap().value() as usize;
2693            assert!(bytes_written > 0);
2694
2695            // Test EVIOCGUNIQ returns ENOENT (synthetic devices have no serial/unique identifier).
2696            let uniq_req = crate::input_file::EVIOCGUNIQ_BASE | (256 << 16);
2697            let res = file.ioctl(&current_task, uniq_req, user_addr.into());
2698            assert_eq!(res, starnix_uapi::error!(ENOENT));
2699
2700            // Test EVIOCGPROP with variable length.
2701            let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
2702            let res = file.ioctl(&current_task, prop_req, user_addr.into());
2703            assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2704
2705            // Test EVIOCGBIT(0) with variable length.
2706            let bit0_req = crate::input_file::EVIOCGBIT_0_BASE | (64 << 16);
2707            let res = file.ioctl(&current_task, bit0_req, user_addr.into());
2708            assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2709
2710            // Test EVIOCGBIT(EV_SND) succeeds and returns zeros.
2711            let snd_req = crate::input_file::EVIOCGBIT_EV_SND_BASE | (64 << 16);
2712            let res = file.ioctl(&current_task, snd_req, user_addr.into());
2713            assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2714
2715            // Test invalid EVIOCGBIT(0x1f) returns EINVAL.
2716            let invalid_bit_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
2717                | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2718                | (64 << 16)
2719                | (0x20 + 0x1f);
2720            let res = file.ioctl(&current_task, invalid_bit_req, user_addr.into());
2721            assert_eq!(res, starnix_uapi::error!(EINVAL));
2722
2723            // Test unrecognized evdev ioctl code with valid type/dir returns EINVAL via fallback.
2724            let unrecognized_evdev_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
2725                | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2726                | 0xFF;
2727            let res = file.ioctl(&current_task, unrecognized_evdev_req, user_addr.into());
2728            assert_eq!(res, starnix_uapi::error!(EINVAL));
2729
2730            // Test invalid direction/type returns EINVAL.
2731            let invalid_dir_req = (uapi::_IOC_WRITE << uapi::_IOC_DIRSHIFT)
2732                | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2733                | 0x20;
2734            let res = file.ioctl(&current_task, invalid_dir_req, user_addr.into());
2735            assert_eq!(res, starnix_uapi::error!(EINVAL));
2736        })
2737        .await;
2738    }
2739
2740    // The evdev current-state queries report what the device is doing right now,
2741    // as opposed to the EVIOCGBIT queries which report what it is capable of.
2742    // Starnix tracks none of this state and reports all-zeros, but these must not
2743    // fail: `xf86-input-evdev` issues all four during PreInit and treats an error
2744    // as fatal, so returning EINVAL here means X11 brings up no input devices.
2745    #[::fuchsia::test]
2746    async fn evdev_current_state_queries_succeed() {
2747        spawn_kernel_and_run(async move |current_task| {
2748            let inspector = fuchsia_inspect::Inspector::default();
2749            let touch_device = InputDevice::new_touch(700, 1200, &inspector.root());
2750            let file =
2751                touch_device.open_test(&current_task).expect("Failed to open touch input file");
2752
2753            let user_addr = starnix_core::testing::map_memory(
2754                &current_task,
2755                starnix_uapi::user_address::UserAddress::default(),
2756                4096,
2757            );
2758
2759            const BUF_LEN: usize = 96;
2760            for (name, base) in [
2761                ("EVIOCGKEY", crate::input_file::EVIOCGKEY_BASE),
2762                ("EVIOCGLED", crate::input_file::EVIOCGLED_BASE),
2763                ("EVIOCGSND", crate::input_file::EVIOCGSND_BASE),
2764                ("EVIOCGSW", crate::input_file::EVIOCGSW_BASE),
2765            ] {
2766                // Poison the buffer first, so that observing zeros afterwards
2767                // proves the ioctl wrote them rather than finding them.
2768                current_task
2769                    .write_memory(user_addr, &[0xAAu8; BUF_LEN])
2770                    .expect("failed to poison buffer");
2771
2772                let req = base | ((BUF_LEN as u32) << 16);
2773                let res = file.ioctl(&current_task, req, user_addr.into());
2774                assert_eq!(res, Ok(starnix_syscalls::SUCCESS), "{name} should succeed");
2775
2776                let read_back = current_task
2777                    .read_memory_to_vec(user_addr, BUF_LEN)
2778                    .expect("failed to read back buffer");
2779                assert_eq!(
2780                    read_back,
2781                    vec![0u8; BUF_LEN],
2782                    "{name} should report nothing currently active"
2783                );
2784            }
2785        })
2786        .await;
2787    }
2788
2789    #[::fuchsia::test]
2790    async fn keyboard_input_file_ioctls() {
2791        spawn_kernel_and_run(async move |current_task| {
2792            let inspector = fuchsia_inspect::Inspector::default();
2793            let keyboard_device = InputDevice::new_keyboard(&inspector.root());
2794            let file = keyboard_device
2795                .open_test(&current_task)
2796                .expect("Failed to open keyboard input file");
2797
2798            let user_addr = map_memory(&current_task, UserAddress::default(), 4096);
2799
2800            // Test EVIOCGNAME
2801            let name_req = crate::input_file::EVIOCGNAME_BASE | (256 << 16);
2802            let res = file.ioctl(&current_task, name_req, user_addr.into());
2803            assert!(res.is_ok());
2804
2805            // Test EVIOCGPROP - should have INPUT_PROP_DIRECT, not INPUT_PROP_POINTER
2806            let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
2807            let res = file.ioctl(&current_task, prop_req, user_addr.into());
2808            assert_eq!(res, Ok(SUCCESS));
2809            let prop_buf = current_task.read_memory_to_vec(user_addr, 8).unwrap();
2810            let byte_idx = (uapi::INPUT_PROP_DIRECT / 8) as usize;
2811            let bit_idx = uapi::INPUT_PROP_DIRECT % 8;
2812            assert_ne!(prop_buf[byte_idx] & (1 << bit_idx), 0);
2813
2814            // Test EVIOCGBIT_EV_KEY - should have KEY_A, KEY_1, KEY_ENTER, KEY_POWER, etc.
2815            let key_req = crate::input_file::EVIOCGBIT_EV_KEY_BASE | (64 << 16);
2816            let res = file.ioctl(&current_task, key_req, user_addr.into());
2817            assert_eq!(res, Ok(SUCCESS));
2818            let key_buf = current_task.read_memory_to_vec(user_addr, 64).unwrap();
2819
2820            let check_key = |key_code: u32| {
2821                let byte = (key_code / 8) as usize;
2822                let bit = key_code % 8;
2823                assert_ne!(
2824                    key_buf[byte] & (1 << bit),
2825                    0,
2826                    "Keycode {} should be set in evdev bitmask",
2827                    key_code
2828                );
2829            };
2830            check_key(uapi::KEY_A);
2831            check_key(uapi::KEY_Z);
2832            check_key(uapi::KEY_1);
2833            check_key(uapi::KEY_ENTER);
2834            check_key(uapi::KEY_SPACE);
2835            check_key(uapi::KEY_ESC);
2836            check_key(uapi::KEY_POWER);
2837            check_key(uapi::KEY_VOLUMEUP);
2838            check_key(uapi::KEY_VOLUMEDOWN);
2839
2840            // Buttons that are not keyboard keys must NOT be advertised. `KEY_MAP` carries a
2841            // block of test-only keycodes (b/311425670) covering BTN_JOYSTICK..BTN_THUMBR; if
2842            // those leak into the capability bitmask, evdev clients classify this device as a
2843            // gamepad as well as a keyboard.
2844            let check_key_absent = |key_code: u32, name: &str| {
2845                let byte = (key_code / 8) as usize;
2846                let bit = key_code % 8;
2847                assert_eq!(
2848                    key_buf[byte] & (1 << bit),
2849                    0,
2850                    "{} ({}) should NOT be set in the keyboard evdev bitmask",
2851                    name,
2852                    key_code
2853                );
2854            };
2855            check_key_absent(uapi::BTN_LEFT, "BTN_LEFT");
2856            check_key_absent(uapi::BTN_TRIGGER, "BTN_TRIGGER");
2857            check_key_absent(uapi::BTN_SOUTH, "BTN_SOUTH");
2858            check_key_absent(uapi::BTN_THUMBR, "BTN_THUMBR");
2859        })
2860        .await;
2861    }
2862
2863    #[::fuchsia::test]
2864    async fn keyboard_events_update_inspect_counters() {
2865        spawn_kernel_and_run(async move |current_task| {
2866            let inspector = fuchsia_inspect::Inspector::default();
2867            let (_keyboard_device, keyboard_file, keyboard_listener) =
2868                start_keyboard_input_inspect(&current_task, &inspector).await;
2869
2870            let key_event = fuiinput::KeyEvent {
2871                timestamp: Some(12345),
2872                type_: Some(fuiinput::KeyEventType::Pressed),
2873                key: Some(fidl_fuchsia_input::Key::A),
2874                ..Default::default()
2875            };
2876
2877            let _ = keyboard_listener.on_key_event(&key_event).await;
2878            std::mem::drop(keyboard_listener);
2879
2880            // Read events to verify delivery
2881            let events = read_uapi_events(&keyboard_file, &current_task);
2882            assert_eq!(events.len(), 2);
2883
2884            // `converted` counts FIDL events, so one key event is 1 here even though it
2885            // produces 2 uapi events (the key plus a SYN). Keep this distinct from
2886            // `total_uapi_events_generated_count`.
2887            assert_data_tree!(inspector, root: {
2888                keyboard_device: {
2889                    active_wake_leases_count: AnyProperty,
2890                    total_events_with_wake_lease_count: AnyProperty,
2891                    total_fidl_events_received_count: 1u64,
2892                    total_fidl_events_ignored_count: 0u64,
2893                    total_fidl_events_unexpected_count: 0u64,
2894                    total_fidl_events_converted_count: 1u64,
2895                    total_uapi_events_generated_count: 2u64,
2896                    last_generated_uapi_event_timestamp_ns: 12345i64,
2897                    keyboard_file_0: {
2898                        fidl_events_received_count: 1u64,
2899                        fidl_events_ignored_count: 0u64,
2900                        fidl_events_unexpected_count: 0u64,
2901                        fidl_events_converted_count: 1u64,
2902                        uapi_events_generated_count: 2u64,
2903                        uapi_events_read_count: 2u64,
2904                        fd_read_count: AnyProperty,
2905                        fd_notify_count: AnyProperty,
2906                        last_generated_uapi_event_timestamp_ns: 12345i64,
2907                        last_read_uapi_event_timestamp_ns: AnyProperty,
2908                        opened_without_nonblock: AnyProperty,
2909                        open_timestamp_ns: AnyProperty,
2910                        closed: AnyProperty,
2911                        close_timestamp_ns: AnyProperty,
2912                    }
2913                }
2914            });
2915        })
2916        .await;
2917    }
2918}