1use crate::InputFile;
6use crate::input_event_relay::OpenedFiles;
7use futures::FutureExt;
8use starnix_core::device::kobject::{Device, DeviceMetadata};
9use starnix_core::device::{DeviceMode, DeviceOps};
10use starnix_core::fs::sysfs::build_device_directory;
11use starnix_core::task::{CurrentTask, Kernel};
12use starnix_core::vfs::pseudo::simple_file::BytesFile;
13use starnix_core::vfs::{FileOps, NamespaceNode};
14use starnix_sync::{InputDeviceFileNodesLock, InputDeviceInfoLock, LockDepMutex};
15use starnix_uapi::device_id::{DeviceId as StarnixDeviceId, INPUT_MAJOR};
16use starnix_uapi::errors::Errno;
17use starnix_uapi::file_mode::mode;
18use starnix_uapi::input_id;
19use starnix_uapi::open_flags::OpenFlags;
20use std::sync::Arc;
21use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
22
23#[derive(Clone)]
24enum InputDeviceId {
25 Touch(i32, i32),
27
28 Keyboard,
30
31 Mouse,
33}
34
35pub struct InputDeviceStatus {
42 pub node: fuchsia_inspect::Node,
44
45 pub file_nodes: LockDepMutex<Vec<fuchsia_inspect::Node>, InputDeviceFileNodesLock>,
48
49 pub total_fidl_events_received_count: AtomicU64,
60
61 pub total_fidl_events_ignored_count: AtomicU64,
64
65 pub total_fidl_events_unexpected_count: AtomicU64,
69
70 pub total_fidl_events_converted_count: AtomicU64,
73
74 pub total_uapi_events_generated_count: AtomicU64,
76
77 pub last_generated_uapi_event_timestamp_ns: AtomicI64,
79
80 pub total_events_with_wake_lease_count: AtomicU64,
82
83 pub active_wake_leases_count: AtomicU64,
85}
86
87impl InputDeviceStatus {
88 pub fn new(node: fuchsia_inspect::Node) -> Arc<Self> {
89 let status = Arc::new(Self {
90 node,
91 file_nodes: Default::default(),
92 total_fidl_events_received_count: AtomicU64::new(0),
93 total_fidl_events_ignored_count: AtomicU64::new(0),
94 total_fidl_events_unexpected_count: AtomicU64::new(0),
95 total_fidl_events_converted_count: AtomicU64::new(0),
96 total_uapi_events_generated_count: AtomicU64::new(0),
97 last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
98 total_events_with_wake_lease_count: AtomicU64::new(0),
99 active_wake_leases_count: AtomicU64::new(0),
100 });
101
102 let weak_status = Arc::downgrade(&status);
103 status.node.record_lazy_values("status", move || {
104 let status = weak_status.upgrade();
105 async move {
106 let inspector = fuchsia_inspect::Inspector::default();
107 if let Some(status) = status {
108 let root = inspector.root();
109 root.record_uint(
110 "total_fidl_events_received_count",
111 status.total_fidl_events_received_count.load(Ordering::Relaxed),
112 );
113 root.record_uint(
114 "total_fidl_events_ignored_count",
115 status.total_fidl_events_ignored_count.load(Ordering::Relaxed),
116 );
117 root.record_uint(
118 "total_fidl_events_unexpected_count",
119 status.total_fidl_events_unexpected_count.load(Ordering::Relaxed),
120 );
121 root.record_uint(
122 "total_fidl_events_converted_count",
123 status.total_fidl_events_converted_count.load(Ordering::Relaxed),
124 );
125 root.record_uint(
126 "total_uapi_events_generated_count",
127 status.total_uapi_events_generated_count.load(Ordering::Relaxed),
128 );
129 root.record_int(
130 "last_generated_uapi_event_timestamp_ns",
131 status.last_generated_uapi_event_timestamp_ns.load(Ordering::Relaxed),
132 );
133 root.record_uint(
134 "total_events_with_wake_lease_count",
135 status.total_events_with_wake_lease_count.load(Ordering::Relaxed),
136 );
137 root.record_uint(
138 "active_wake_leases_count",
139 status.active_wake_leases_count.load(Ordering::Relaxed),
140 );
141 }
142 Ok(inspector)
143 }
144 .boxed()
145 });
146
147 status
148 }
149
150 pub fn count_total_received_events(&self, count: u64) {
151 self.total_fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
152 }
153
154 pub fn count_events_with_wake_lease(&self, count: u64) {
155 self.total_events_with_wake_lease_count.fetch_add(count, Ordering::Relaxed);
156 }
157
158 pub fn increment_active_wake_leases(&self, count: u64) {
159 self.active_wake_leases_count.fetch_add(count, Ordering::Relaxed);
160 }
161
162 pub fn decrement_active_wake_leases(&self, count: u64) {
163 self.active_wake_leases_count.fetch_sub(count, Ordering::Relaxed);
164 }
165
166 pub fn count_total_ignored_events(&self, count: u64) {
167 self.total_fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
168 }
169
170 pub fn count_total_unexpected_events(&self, count: u64) {
171 self.total_fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
172 }
173
174 pub fn count_total_converted_events(&self, count: u64) {
175 self.total_fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
176 }
177
178 pub fn count_total_generated_events(&self, count: u64, event_time_ns: i64) {
179 self.total_uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
180 self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
181 }
182}
183
184#[derive(Clone, Debug, PartialEq)]
185pub struct InputDeviceInfo {
186 pub input_id: input_id,
187 pub name: String,
188}
189
190pub type InputDeviceInfoHandle = Arc<LockDepMutex<InputDeviceInfo, InputDeviceInfoLock>>;
191
192impl InputDeviceInfo {
193 pub fn new(input_id: input_id, name: String) -> InputDeviceInfoHandle {
194 Arc::new(LockDepMutex::new(Self { input_id, name }))
195 }
196}
197
198#[derive(Clone)]
199pub struct InputDevice {
200 device_type: InputDeviceId,
201
202 pub open_files: OpenedFiles,
203
204 pub inspect_status: Arc<InputDeviceStatus>,
205
206 pub info: InputDeviceInfoHandle,
207}
208
209impl InputDevice {
210 pub fn new_touch(
211 display_width: i32,
212 display_height: i32,
213 info: InputDeviceInfoHandle,
214 node_name: &str,
215 inspect_node: &fuchsia_inspect::Node,
216 ) -> Self {
217 let node = inspect_node.create_child(node_name);
218 InputDevice {
219 device_type: InputDeviceId::Touch(display_width, display_height),
220 open_files: Default::default(),
221 inspect_status: InputDeviceStatus::new(node),
222 info,
223 }
224 }
225
226 pub fn new_keyboard(
227 info: InputDeviceInfoHandle,
228 node_name: &str,
229 inspect_node: &fuchsia_inspect::Node,
230 ) -> Self {
231 let node = inspect_node.create_child(node_name);
232 InputDevice {
233 device_type: InputDeviceId::Keyboard,
234 open_files: Default::default(),
235 inspect_status: InputDeviceStatus::new(node),
236 info,
237 }
238 }
239
240 pub fn new_mouse(
241 info: InputDeviceInfoHandle,
242 node_name: &str,
243 inspect_node: &fuchsia_inspect::Node,
244 ) -> Self {
245 let node = inspect_node.create_child(node_name);
246 InputDevice {
247 device_type: InputDeviceId::Mouse,
248 open_files: Default::default(),
249 inspect_status: InputDeviceStatus::new(node),
250 info,
251 }
252 }
253
254 pub fn register(self, kernel: &Kernel, minor: u32) -> Result<Device, Errno> {
279 let registry = &kernel.device_registry;
280 let input_class = registry.objects.input_class();
281 let info = self.info.clone();
282
283 let input_device = registry.add_numberless_device(
284 format!("input{minor}").as_str().into(),
285 None,
286 input_class.clone(),
287 move |device, dir| {
288 build_device_directory(device, dir);
289 let name_info = info.clone();
290 dir.entry(
291 "name",
292 BytesFile::new_node(move || Ok(format!("{}\n", name_info.lock().name))),
293 mode!(IFREG, 0o444),
294 );
295 dir.subdir("id", 0o755, |dir| {
296 let fields: [(&str, fn(&input_id) -> u16); 4] = [
297 ("bustype", |id| id.bustype),
298 ("vendor", |id| id.vendor),
299 ("product", |id| id.product),
300 ("version", |id| id.version),
301 ];
302 for (attribute, selector) in fields {
303 let attr_info = info.clone();
304 dir.entry(
305 attribute,
306 BytesFile::new_node(move || {
307 Ok(format!("{:04x}\n", selector(&attr_info.lock().input_id)))
308 }),
309 mode!(IFREG, 0o444),
310 );
311 }
312 });
313 },
314 );
315
316 registry.register_device_with_dir(
317 kernel,
318 format!("event{minor}").as_str().into(),
319 DeviceMetadata::new(
320 format!("input/event{minor}").into(),
321 StarnixDeviceId::new(INPUT_MAJOR, minor),
322 DeviceMode::Char,
323 ),
324 Some(input_device),
325 input_class,
326 build_device_directory,
327 self,
328 )
329 }
330
331 pub fn unregister(current_task: &CurrentTask, event_device: Device) {
336 let registry = ¤t_task.kernel().device_registry;
337 let input_device = event_device.parent().cloned();
338 registry.remove_device(current_task, event_device);
339 if let Some(input_device) = input_device {
340 registry.remove_device(current_task, input_device);
341 }
342 }
343
344 pub fn open_internal(&self) -> Box<dyn FileOps> {
345 let input_file = match self.device_type {
346 InputDeviceId::Touch(display_width, display_height) => {
347 let mut file_nodes = self.inspect_status.file_nodes.lock();
348 let child_node = self
349 .inspect_status
350 .node
351 .create_child(format!("touch_file_{}", file_nodes.len()));
352 let file = Arc::new(InputFile::new_touch(
353 self.info.clone(),
354 display_width,
355 display_height,
356 &child_node,
357 ));
358 file_nodes.push(child_node);
359 file
360 }
361 InputDeviceId::Keyboard => {
362 let mut file_nodes = self.inspect_status.file_nodes.lock();
363 let child_node = self
364 .inspect_status
365 .node
366 .create_child(format!("keyboard_file_{}", file_nodes.len()));
367 let file = Arc::new(InputFile::new_keyboard(self.info.clone(), &child_node));
368 file_nodes.push(child_node);
369 file
370 }
371 InputDeviceId::Mouse => {
372 let mut file_nodes = self.inspect_status.file_nodes.lock();
373 let child_node = self
374 .inspect_status
375 .node
376 .create_child(format!("mouse_file_{}", file_nodes.len()));
377 let file = Arc::new(InputFile::new_mouse(self.info.clone(), &child_node));
378 file_nodes.push(child_node);
379 file
380 }
381 };
382 input_file.init_inspect_status();
383 self.open_files.lock().on_file_opened(&input_file);
384 Box::new(crate::input_file::ArcInputFile(input_file))
385 }
386
387 #[cfg(test)]
388 pub fn open_test(
389 &self,
390 current_task: &CurrentTask,
391 ) -> Result<starnix_core::vfs::FileHandle, Errno> {
392 let input_file = self.open_internal();
393 let root_namespace_node = current_task
394 .lookup_path_from_root(".".into())
395 .expect("failed to get namespace node for root");
396
397 let file_object = starnix_core::vfs::FileObject::new(
398 current_task,
399 input_file,
400 root_namespace_node,
401 OpenFlags::empty(),
402 )
403 .expect("FileObject::new failed");
404 Ok(file_object)
405 }
406}
407
408impl DeviceOps for InputDevice {
409 fn open(
410 &self,
411 _current_task: &CurrentTask,
412 _id: StarnixDeviceId,
413 _node: &NamespaceNode,
414 _flags: OpenFlags,
415 ) -> Result<Box<dyn FileOps>, Errno> {
416 let input_file = self.open_internal();
417 Ok(input_file)
418 }
419}
420
421#[cfg(test)]
422mod test {
423 #![allow(clippy::unused_unit)] use super::*;
426 use crate::input_event_relay::{
427 self, EventProxyMode, KEYBOARD_INPUT_ID, MOUSE_INPUT_ID, StartRelaysArgs, TOUCH_INPUT_ID,
428 };
429 use anyhow::anyhow;
430 use assert_matches::assert_matches;
431 use diagnostics_assertions::{AnyProperty, assert_data_tree};
432 use fidl::endpoints::RequestStream as _;
433 use fidl_fuchsia_ui_input as fuiinput;
434 use fidl_fuchsia_ui_input::MediaButtonsEvent;
435 use fidl_fuchsia_ui_input3 as fuiinput3;
436 use fidl_fuchsia_ui_pointer as fuipointer;
437 use fidl_fuchsia_ui_policy as fuipolicy;
438 use fuchsia_async::DurationExt;
439 use fuipointer::{
440 EventPhase, TouchEvent, TouchInteractionId, TouchPointerSample, TouchSourceV2Marker,
441 };
442 use futures::StreamExt as _;
443 use pretty_assertions::assert_eq;
444 use starnix_core::mm::{MemoryAccessor as _, MemoryAccessorExt as _};
445 use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
446 use starnix_core::task::{EventHandler, Waiter};
447 use starnix_core::testing::{map_memory, spawn_kernel_and_run};
448 use starnix_core::vfs::FileHandle;
449 use starnix_core::vfs::buffers::VecOutputBuffer;
450 use starnix_syscalls::SUCCESS;
451 use starnix_types::time::timeval_from_time;
452 use starnix_uapi::errors::EAGAIN;
453 use starnix_uapi::uapi;
454 use starnix_uapi::user_address::UserAddress;
455 use starnix_uapi::vfs::FdEvents;
456
457 use test_case::test_case;
458 use test_util::assert_near;
459 use zerocopy::FromBytes as _;
460
461 const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
462 async fn start_touch_input(
463 current_task: &CurrentTask,
464 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
465 let inspector = fuchsia_inspect::Inspector::default();
466 start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
467 }
468
469 async fn start_touch_input_inspect(
470 current_task: &CurrentTask,
471 inspector: &fuchsia_inspect::Inspector,
472 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
473 start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
474 }
475
476 async fn init_keyboard_listener(
477 keyboard_stream: &mut fuiinput3::KeyboardRequestStream,
478 ) -> fuiinput3::KeyboardListenerProxy {
479 let keyboard_listener = match keyboard_stream.next().await {
480 Some(Ok(fuiinput3::KeyboardRequest::AddListener {
481 view_ref: _,
482 listener,
483 responder,
484 })) => {
485 let _ = responder.send();
486 listener.into_proxy()
487 }
488 _ => {
489 panic!("Failed to get event");
490 }
491 };
492
493 keyboard_listener
494 }
495
496 async fn init_button_listeners(
497 device_listener_stream: &mut fuipolicy::DeviceListenerRegistryRequestStream,
498 ) -> (fuipolicy::MediaButtonsListenerProxy, fuipolicy::TouchButtonsListenerProxy) {
499 let media_buttons_listener = match device_listener_stream.next().await {
500 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
501 listener,
502 responder,
503 })) => {
504 let _ = responder.send();
505 listener.into_proxy()
506 }
507 _ => {
508 panic!("Failed to get event");
509 }
510 };
511
512 let touch_buttons_listener = match device_listener_stream.next().await {
513 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
514 listener,
515 responder,
516 })) => {
517 let _ = responder.send();
518 listener.into_proxy()
519 }
520 _ => {
521 panic!("Failed to get event");
522 }
523 };
524
525 (media_buttons_listener, touch_buttons_listener)
526 }
527
528 async fn init_device_listener(
529 device_listener_registry_stream: &mut fuiinput::DeviceListenerRegistryRequestStream,
530 ) -> (fuiinput::DeviceListenerProxy, fuiinput::DeviceIteratorRequestStream) {
531 let (device_iterator_client_end, device_iterator_stream) =
532 fidl::endpoints::create_request_stream::<fuiinput::DeviceIteratorMarker>();
533 let device_listener_proxy = match device_listener_registry_stream.next().await {
534 Some(Ok(fuiinput::DeviceListenerRegistryRequest::RegisterListener {
535 listener,
536 responder,
537 })) => {
538 let _ = responder.send(device_iterator_client_end);
539 listener.into_proxy()
540 }
541 _ => {
542 panic!("Failed to initialize device listener");
543 }
544 };
545 (device_listener_proxy, device_iterator_stream)
546 }
547
548 async fn drain_empty_device_iterator(
549 device_iterator_stream: &mut fuiinput::DeviceIteratorRequestStream,
550 ) {
551 match device_iterator_stream.next().await {
552 Some(Ok(fuiinput::DeviceIteratorRequest::GetNext { responder })) => {
553 responder.send(&[]).expect("failed to send empty batch");
554 }
555 _ => panic!("Failed to respond to existing devices iterator GetNext"),
556 }
557 }
558
559 async fn start_touch_input_inspect_and_dimensions(
560 current_task: &CurrentTask,
561 x_max: i32,
562 y_max: i32,
563 inspector: &fuchsia_inspect::Inspector,
564 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
565 let input_device = InputDevice::new_touch(
566 x_max,
567 y_max,
568 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
569 "touch_device",
570 inspector.root(),
571 );
572 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
573
574 let (touch_source_client_end, touch_source_stream) =
575 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
576
577 let (mouse_source_client_end, _mouse_source_stream) =
578 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
579
580 let (keyboard_proxy, mut keyboard_stream) =
581 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
582 let view_ref_pair =
583 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
584
585 let (device_registry_proxy, mut device_listener_stream) =
586 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
587 );
588
589 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
590 relay.add_touch_device(
591 input_event_relay::DEFAULT_TOUCH_DEVICE_ID,
592 input_device.open_files.clone(),
593 Some(input_device.inspect_status.clone()),
594 );
595 relay.start_relays(
596 ¤t_task.kernel(),
597 StartRelaysArgs {
598 event_proxy_mode: EventProxyMode::None,
599 touch_source_client_end,
600 keyboard_proxy,
601 mouse_source_client_end,
602 view_ref: view_ref_pair.view_ref,
603 registry_proxy: device_registry_proxy,
604 device_listener_registry: None,
605 display_width: x_max,
606 display_height: y_max,
607 },
608 );
609
610 let _ = init_keyboard_listener(&mut keyboard_stream).await;
611 let _ = init_button_listeners(&mut device_listener_stream).await;
612
613 (input_device, input_file, touch_source_stream)
614 }
615
616 async fn start_keyboard_input(
617 current_task: &CurrentTask,
618 ) -> (InputDevice, FileHandle, fuiinput3::KeyboardListenerProxy) {
619 let inspector = fuchsia_inspect::Inspector::default();
620 start_keyboard_input_inspect(current_task, &inspector).await
621 }
622
623 async fn start_keyboard_input_inspect(
624 current_task: &CurrentTask,
625 inspector: &fuchsia_inspect::Inspector,
626 ) -> (InputDevice, FileHandle, fuiinput3::KeyboardListenerProxy) {
627 let input_device = InputDevice::new_keyboard(
628 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
629 "keyboard_device",
630 inspector.root(),
631 );
632 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
633 let (keyboard_proxy, mut keyboard_stream) =
634 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
635 let view_ref_pair =
636 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
637
638 let (device_registry_proxy, mut device_listener_stream) =
639 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
640 );
641
642 let (touch_source_client_end, _touch_source_stream) =
643 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
644
645 let (mouse_source_client_end, _mouse_source_stream) =
646 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
647
648 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
649 relay.add_keyboard_device(
650 input_event_relay::DEFAULT_KEYBOARD_DEVICE_ID,
651 input_device.open_files.clone(),
652 Some(input_device.inspect_status.clone()),
653 );
654 relay.start_relays(
655 current_task.kernel(),
656 StartRelaysArgs {
657 event_proxy_mode: EventProxyMode::None,
658 touch_source_client_end,
659 keyboard_proxy,
660 mouse_source_client_end,
661 view_ref: view_ref_pair.view_ref,
662 registry_proxy: device_registry_proxy,
663 device_listener_registry: None,
664 display_width: 0,
665 display_height: 0,
666 },
667 );
668
669 let keyboard_listener = init_keyboard_listener(&mut keyboard_stream).await;
670 let _ = init_button_listeners(&mut device_listener_stream).await;
671
672 (input_device, input_file, keyboard_listener)
673 }
674
675 async fn start_button_input(
676 current_task: &CurrentTask,
677 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
678 let inspector = fuchsia_inspect::Inspector::default();
679 start_button_input_inspect(current_task, &inspector).await
680 }
681
682 async fn start_button_input_inspect(
683 current_task: &CurrentTask,
684 inspector: &fuchsia_inspect::Inspector,
685 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
686 let input_device = InputDevice::new_keyboard(
687 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
688 "keyboard_device",
689 inspector.root(),
690 );
691 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
692 let (device_registry_proxy, mut device_listener_stream) =
693 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
694 );
695
696 let (touch_source_client_end, _touch_source_stream) =
697 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
698 let (mouse_source_client_end, _mouse_source_stream) =
699 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
700 let (keyboard_proxy, mut keyboard_stream) =
701 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
702 let view_ref_pair =
703 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
704
705 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
706 relay.add_keyboard_device(
707 input_event_relay::DEFAULT_KEYBOARD_DEVICE_ID,
708 input_device.open_files.clone(),
709 Some(input_device.inspect_status.clone()),
710 );
711 relay.start_relays(
712 current_task.kernel(),
713 StartRelaysArgs {
714 event_proxy_mode: EventProxyMode::None,
715 touch_source_client_end,
716 keyboard_proxy,
717 mouse_source_client_end,
718 view_ref: view_ref_pair.view_ref,
719 registry_proxy: device_registry_proxy,
720 device_listener_registry: None,
721 display_width: 0,
722 display_height: 0,
723 },
724 );
725
726 let _ = init_keyboard_listener(&mut keyboard_stream).await;
727 let (button_listener, _) = init_button_listeners(&mut device_listener_stream).await;
728
729 (input_device, input_file, button_listener)
730 }
731
732 async fn start_mouse_input(
733 current_task: &CurrentTask,
734 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
735 let inspector = fuchsia_inspect::Inspector::default();
736 start_mouse_input_inspect(current_task, &inspector).await
737 }
738
739 async fn start_mouse_input_inspect(
740 current_task: &CurrentTask,
741 inspector: &fuchsia_inspect::Inspector,
742 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
743 let input_device = InputDevice::new_mouse(
744 InputDeviceInfo::new(MOUSE_INPUT_ID, "starnix_mouse".to_string()),
745 "mouse_device",
746 inspector.root(),
747 );
748 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
749
750 let (touch_source_client_end, _touch_source_stream) =
751 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
752
753 let (mouse_source_client_end, mouse_source_stream) =
754 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
755
756 let (keyboard_proxy, mut keyboard_stream) =
757 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
758 let view_ref_pair =
759 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
760
761 let (device_registry_proxy, mut device_listener_stream) =
762 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
763 );
764
765 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
766 relay.add_mouse_device(
767 input_event_relay::DEFAULT_MOUSE_DEVICE_ID,
768 input_device.open_files.clone(),
769 Some(input_device.inspect_status.clone()),
770 );
771 relay.start_relays(
772 ¤t_task.kernel(),
773 StartRelaysArgs {
774 event_proxy_mode: EventProxyMode::None,
775 touch_source_client_end,
776 keyboard_proxy,
777 mouse_source_client_end,
778 view_ref: view_ref_pair.view_ref,
779 registry_proxy: device_registry_proxy,
780 device_listener_registry: None,
781 display_width: 0,
782 display_height: 0,
783 },
784 );
785
786 let _ = init_keyboard_listener(&mut keyboard_stream).await;
787 let _ = init_button_listeners(&mut device_listener_stream).await;
788
789 (input_device, input_file, mouse_source_stream)
790 }
791
792 fn make_touch_event(pointer_id: u32) -> fuipointer::TouchEvent {
793 make_touch_event_with_phase(EventPhase::Change, pointer_id)
795 }
796
797 fn make_touch_event_with_phase(phase: EventPhase, pointer_id: u32) -> fuipointer::TouchEvent {
798 make_touch_event_with_coords_phase(0.0, 0.0, phase, pointer_id)
799 }
800
801 fn make_touch_event_with_coords_phase(
802 x: f32,
803 y: f32,
804 phase: EventPhase,
805 pointer_id: u32,
806 ) -> fuipointer::TouchEvent {
807 make_touch_event_with_coords_phase_timestamp(x, y, phase, pointer_id, 0)
808 }
809
810 fn make_touch_event_with_coords(x: f32, y: f32, pointer_id: u32) -> fuipointer::TouchEvent {
811 make_touch_event_with_coords_phase(x, y, EventPhase::Change, pointer_id)
812 }
813
814 fn make_touch_event_with_coords_phase_timestamp(
815 x: f32,
816 y: f32,
817 phase: EventPhase,
818 pointer_id: u32,
819 time_nanos: i64,
820 ) -> fuipointer::TouchEvent {
821 make_touch_event_with_coords_phase_timestamp_device_id(
822 x, y, phase, pointer_id, time_nanos, 0,
823 )
824 }
825
826 fn make_empty_touch_event() -> fuipointer::TouchEvent {
827 TouchEvent {
828 pointer_sample: Some(TouchPointerSample {
829 interaction: Some(TouchInteractionId {
830 pointer_id: 0,
831 device_id: 0,
832 interaction_id: 0,
833 }),
834 ..Default::default()
835 }),
836 ..Default::default()
837 }
838 }
839
840 fn make_touch_event_with_coords_phase_timestamp_device_id(
841 x: f32,
842 y: f32,
843 phase: EventPhase,
844 pointer_id: u32,
845 time_nanos: i64,
846 device_id: u32,
847 ) -> fuipointer::TouchEvent {
848 TouchEvent {
849 timestamp: Some(time_nanos),
850 pointer_sample: Some(TouchPointerSample {
851 position_in_viewport: Some([x, y]),
852 phase: Some(phase),
854 interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
855 ..Default::default()
856 }),
857 ..Default::default()
858 }
859 }
860
861 fn make_mouse_wheel_event(ticks: i64) -> fuipointer::MouseEvent {
862 make_mouse_wheel_event_with_timestamp(ticks, 0)
863 }
864
865 fn make_mouse_wheel_event_with_timestamp(ticks: i64, timestamp: i64) -> fuipointer::MouseEvent {
866 fuipointer::MouseEvent {
867 timestamp: Some(timestamp),
868 pointer_sample: Some(fuipointer::MousePointerSample {
869 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
870 scroll_v: Some(ticks),
871 ..Default::default()
872 }),
873 ..Default::default()
874 }
875 }
876
877 fn read_uapi_events(file: &FileHandle, current_task: &CurrentTask) -> Vec<uapi::input_event> {
878 std::iter::from_fn(|| {
879 let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
880 match file.read(current_task, &mut event_bytes) {
881 Ok(INPUT_EVENT_SIZE) => Some(
882 uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
883 .map_err(|_| anyhow!("failed to read input_event from buffer")),
884 ),
885 Ok(other_size) => {
886 Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
887 }
888 Err(Errno { code: EAGAIN, .. }) => None,
889 Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
890 }
891 })
892 .enumerate()
893 .map(|(i, read_res)| match read_res {
894 Ok(event) => event,
895 Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
896 })
897 .collect()
898 }
899
900 async fn answer_next_touch_watch_request(
902 request_stream: &mut fuipointer::TouchSourceV2RequestStream,
903 touch_events: Vec<TouchEvent>,
904 ) {
905 let control_handle = request_stream.control_handle();
906 control_handle
907 .send_on_touch_events(touch_events, 1)
908 .expect("failure sending OnTouchEvents");
909 match request_stream.next().await {
910 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents { .. })) => {}
911 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
912 }
913 }
914
915 async fn answer_next_mouse_watch_request(
917 request_stream: &mut fuipointer::MouseSourceV2RequestStream,
918 mouse_events: Vec<fuipointer::MouseEvent>,
919 ) {
920 let control_handle = request_stream.control_handle();
921 control_handle
922 .send_on_mouse_events(mouse_events, 1)
923 .expect("failure sending OnMouseEvents");
924 match request_stream.next().await {
925 Some(Ok(fuipointer::MouseSourceV2Request::AcknowledgeEvents { .. })) => {}
926 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
927 }
928 }
929
930 #[::fuchsia::test]
931 async fn touch_source_v2_acknowledges_events_with_matching_stamp() {
932 spawn_kernel_and_run(async move |current_task| {
933 let (_input_device, _input_file, mut touch_source_stream) =
934 start_touch_input(¤t_task).await;
935
936 let control_handle = touch_source_stream.control_handle();
937 control_handle
938 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 10)
939 .expect("failure sending OnTouchEvents");
940 match touch_source_stream.next().await {
941 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
942 last_acknowledged_event_stamp,
943 ..
944 })) => assert_eq!(last_acknowledged_event_stamp, 10),
945 unexpected => panic!("unexpected request {:?}", unexpected),
946 }
947
948 control_handle
949 .send_on_touch_events(
950 vec![
951 make_empty_touch_event(),
952 make_empty_touch_event(),
953 make_empty_touch_event(),
954 make_empty_touch_event(),
955 make_empty_touch_event(),
956 ],
957 20,
958 )
959 .expect("failure sending OnTouchEvents");
960 match touch_source_stream.next().await {
961 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
962 last_acknowledged_event_stamp,
963 ..
964 })) => assert_eq!(last_acknowledged_event_stamp, 20),
965 unexpected => panic!("unexpected request {:?}", unexpected),
966 }
967 })
968 .await;
969 }
970
971 #[::fuchsia::test]
972 async fn notifies_polling_waiters_of_new_data() {
973 spawn_kernel_and_run(async move |current_task| {
974 let (_input_device, input_file, mut touch_source_stream) =
977 start_touch_input(¤t_task).await;
978 let waiter1 = Waiter::new();
979 let waiter2 = Waiter::new();
980
981 [&waiter1, &waiter2].iter().for_each(|waiter| {
983 input_file.wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None);
984 });
985 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
986 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
987
988 answer_next_touch_watch_request(
990 &mut touch_source_stream,
991 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
992 )
993 .await;
994 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
995 .await;
996
997 assert_eq!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1001 assert_eq!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1002 })
1003 .await;
1004 }
1005
1006 #[::fuchsia::test]
1007 async fn notifies_blocked_waiter_of_new_data() {
1008 spawn_kernel_and_run(async move |current_task| {
1009 let kernel = current_task.kernel().clone();
1011 let (_input_device, input_file, mut touch_source_stream) =
1012 start_touch_input(¤t_task).await;
1013 let waiter = Waiter::new();
1014
1015 input_file.wait_async(¤t_task, &waiter, FdEvents::POLLIN, EventHandler::None);
1017
1018 let closure = move |task: &CurrentTask| waiter.wait(&task);
1019
1020 let (waiter_thread, req) = SpawnRequestBuilder::new()
1021 .with_debug_name("input-device-waiter")
1022 .with_sync_closure(closure)
1023 .build_with_async_result();
1024 kernel.kthreads.spawner().spawn_from_request(req);
1025
1026 let mut waiter_thread = Box::pin(waiter_thread);
1027 assert_matches!(futures::poll!(&mut waiter_thread), futures::task::Poll::Pending);
1028
1029 answer_next_touch_watch_request(
1031 &mut touch_source_stream,
1032 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1033 )
1034 .await;
1035
1036 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1042 .await;
1043 })
1044 .await;
1045 }
1046
1047 #[::fuchsia::test]
1048 async fn does_not_notify_polling_waiters_without_new_data() {
1049 spawn_kernel_and_run(async move |current_task| {
1050 let (_input_device, input_file, mut touch_source_stream) =
1053 start_touch_input(¤t_task).await;
1054 let waiter1 = Waiter::new();
1055 let waiter2 = Waiter::new();
1056
1057 [&waiter1, &waiter2].iter().for_each(|waiter| {
1059 input_file.wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None);
1060 });
1061 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1062 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1063
1064 answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
1066
1067 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1071 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1072 })
1073 .await;
1074 }
1075
1076 #[::fuchsia::test]
1089 async fn honors_wait_cancellation() {
1090 spawn_kernel_and_run(async move |current_task| {
1091 let (_input_device, input_file, mut touch_source_stream) =
1094 start_touch_input(¤t_task).await;
1095 let waiter1 = Waiter::new();
1096 let waiter2 = Waiter::new();
1097
1098 let waitkeys = [&waiter1, &waiter2]
1100 .iter()
1101 .map(|waiter| {
1102 input_file
1103 .wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None)
1104 .expect("wait_async")
1105 })
1106 .collect::<Vec<_>>();
1107
1108 waitkeys.into_iter().next().expect("failed to get first waitkey").cancel();
1110
1111 answer_next_touch_watch_request(
1113 &mut touch_source_stream,
1114 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1115 )
1116 .await;
1117 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1119 .await;
1120
1121 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1125 assert_eq!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1126 })
1127 .await;
1128 }
1129
1130 #[::fuchsia::test]
1131 async fn query_events() {
1132 spawn_kernel_and_run(async move |current_task| {
1133 let (_input_device, input_file, mut touch_source_stream) =
1136 start_touch_input(¤t_task).await;
1137
1138 assert_eq!(
1140 input_file.query_events(¤t_task).expect("query_events"),
1141 FdEvents::empty(),
1142 "events should be empty before data arrives"
1143 );
1144
1145 answer_next_touch_watch_request(
1147 &mut touch_source_stream,
1148 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1149 )
1150 .await;
1151
1152 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1154 .await;
1155
1156 assert_eq!(
1158 input_file.query_events(¤t_task).expect("query_events"),
1159 FdEvents::POLLIN | FdEvents::POLLRDNORM,
1160 "events should be POLLIN after data arrives"
1161 );
1162 })
1163 .await;
1164 }
1165
1166 fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1167 make_uapi_input_event_with_timestamp(ty, code, value, 0)
1168 }
1169
1170 fn make_uapi_input_event_with_timestamp(
1171 ty: u32,
1172 code: u32,
1173 value: i32,
1174 time_nanos: i64,
1175 ) -> uapi::input_event {
1176 uapi::input_event {
1177 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
1178 type_: ty as u16,
1179 code: code as u16,
1180 value,
1181 }
1182 }
1183
1184 #[::fuchsia::test]
1185 async fn touch_event_ignored() {
1186 spawn_kernel_and_run(async move |current_task| {
1187 let inspector = fuchsia_inspect::Inspector::default();
1189
1190 let (_input_device, input_file, mut touch_source_stream) =
1191 start_touch_input_inspect(¤t_task, &inspector).await;
1192
1193 answer_next_touch_watch_request(
1196 &mut touch_source_stream,
1197 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1198 )
1199 .await;
1200
1201 answer_next_touch_watch_request(
1205 &mut touch_source_stream,
1206 vec![make_empty_touch_event()],
1207 )
1208 .await;
1209
1210 let events = read_uapi_events(&input_file, ¤t_task);
1212
1213 assert_eq!(events.len(), 6);
1214
1215 answer_next_touch_watch_request(
1218 &mut touch_source_stream,
1219 vec![make_empty_touch_event()],
1220 )
1221 .await;
1222
1223 let events = read_uapi_events(&input_file, ¤t_task);
1224 assert_eq!(events, vec![]);
1225 assert_data_tree!(inspector, root: {
1226 touch_device: {
1227 active_wake_leases_count: 0u64,
1228 total_events_with_wake_lease_count: 0u64,
1229 total_fidl_events_received_count: 3u64,
1230 total_fidl_events_ignored_count: 2u64,
1231 total_fidl_events_unexpected_count: 0u64,
1232 total_fidl_events_converted_count: 1u64,
1233 total_uapi_events_generated_count: 6u64,
1234 last_generated_uapi_event_timestamp_ns: 0i64,
1235 touch_file_0: {
1236 fidl_events_received_count: 3u64,
1237 fidl_events_ignored_count: 2u64,
1238 fidl_events_unexpected_count: 0u64,
1239 fidl_events_converted_count: 1u64,
1240 uapi_events_generated_count: 6u64,
1241 uapi_events_read_count: 6u64,
1242 fd_read_count: 8u64,
1243 fd_notify_count: 1u64,
1244 last_generated_uapi_event_timestamp_ns: 0i64,
1245 last_read_uapi_event_timestamp_ns: 0i64,
1246 opened_without_nonblock: AnyProperty,
1247 open_timestamp_ns: AnyProperty,
1248 closed: AnyProperty,
1249 close_timestamp_ns: AnyProperty,
1250 },
1251 }
1252 });
1253 })
1254 .await;
1255 }
1256
1257 #[test_case(make_touch_event_with_phase(EventPhase::Add, 1); "touch add for pointer already added")]
1258 #[test_case(make_touch_event_with_phase(EventPhase::Change, 2); "touch change for pointer not added")]
1259 #[test_case(make_touch_event_with_phase(EventPhase::Remove, 2); "touch remove for pointer not added")]
1260 #[test_case(make_touch_event_with_phase(EventPhase::Cancel, 1); "touch cancel")]
1261 #[::fuchsia::test]
1262 async fn touch_event_unexpected(event: TouchEvent) {
1263 spawn_kernel_and_run(async move |current_task| {
1264 let inspector = fuchsia_inspect::Inspector::default();
1266
1267 let (_input_device, input_file, mut touch_source_stream) =
1268 start_touch_input_inspect(¤t_task, &inspector).await;
1269
1270 answer_next_touch_watch_request(
1273 &mut touch_source_stream,
1274 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1275 )
1276 .await;
1277
1278 answer_next_touch_watch_request(
1282 &mut touch_source_stream,
1283 vec![make_empty_touch_event()],
1284 )
1285 .await;
1286
1287 let events = read_uapi_events(&input_file, ¤t_task);
1289
1290 assert_eq!(events.len(), 6);
1291
1292 answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1295
1296 let events = read_uapi_events(&input_file, ¤t_task);
1297 assert_eq!(events, vec![]);
1298 assert_data_tree!(inspector, root: {
1299 touch_device: {
1300 active_wake_leases_count: 0u64,
1301 total_events_with_wake_lease_count: 0u64,
1302 total_fidl_events_received_count: 3u64,
1303 total_fidl_events_ignored_count: 1u64,
1304 total_fidl_events_unexpected_count: 1u64,
1305 total_fidl_events_converted_count: 1u64,
1306 total_uapi_events_generated_count: 6u64,
1307 last_generated_uapi_event_timestamp_ns: 0i64,
1308 touch_file_0: {
1309 fidl_events_received_count: 3u64,
1310 fidl_events_ignored_count: 1u64,
1311 fidl_events_unexpected_count: 1u64,
1312 fidl_events_converted_count: 1u64,
1313 uapi_events_generated_count: 6u64,
1314 uapi_events_read_count: 6u64,
1315 fd_read_count: 8u64,
1316 fd_notify_count: 1u64,
1317 last_generated_uapi_event_timestamp_ns: 0i64,
1318 last_read_uapi_event_timestamp_ns: 0i64,
1319 opened_without_nonblock: AnyProperty,
1320 open_timestamp_ns: AnyProperty,
1321 closed: AnyProperty,
1322 close_timestamp_ns: AnyProperty,
1323 },
1324 }
1325 });
1326 })
1327 .await;
1328 }
1329
1330 #[::fuchsia::test]
1331 async fn translates_touch_add() {
1332 spawn_kernel_and_run(async move |current_task| {
1333 let (_input_device, input_file, mut touch_source_stream) =
1336 start_touch_input(¤t_task).await;
1337
1338 answer_next_touch_watch_request(
1340 &mut touch_source_stream,
1341 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1342 )
1343 .await;
1344
1345 answer_next_touch_watch_request(
1349 &mut touch_source_stream,
1350 vec![make_empty_touch_event()],
1351 )
1352 .await;
1353
1354 let events = read_uapi_events(&input_file, ¤t_task);
1356
1357 assert_eq!(
1358 events,
1359 vec![
1360 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1361 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1362 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1363 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1364 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1365 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1366 ]
1367 );
1368 })
1369 .await;
1370 }
1371
1372 #[::fuchsia::test]
1373 async fn translates_touch_change() {
1374 spawn_kernel_and_run(async move |current_task| {
1375 let (_input_device, input_file, mut touch_source_stream) =
1378 start_touch_input(¤t_task).await;
1379
1380 answer_next_touch_watch_request(
1382 &mut touch_source_stream,
1383 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1384 )
1385 .await;
1386
1387 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1391 .await;
1392
1393 let events = read_uapi_events(&input_file, ¤t_task);
1395
1396 assert_eq!(events.len(), 6);
1397
1398 answer_next_touch_watch_request(
1400 &mut touch_source_stream,
1401 vec![make_touch_event_with_coords(10.0, 20.0, 1)],
1402 )
1403 .await;
1404
1405 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1407 .await;
1408
1409 let events = read_uapi_events(&input_file, ¤t_task);
1410 assert_eq!(
1411 events,
1412 vec![
1413 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1414 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1415 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1416 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1417 ]
1418 );
1419 })
1420 .await;
1421 }
1422
1423 #[::fuchsia::test]
1424 async fn translates_touch_remove() {
1425 spawn_kernel_and_run(async move |current_task| {
1426 let (_input_device, input_file, mut touch_source_stream) =
1429 start_touch_input(¤t_task).await;
1430
1431 answer_next_touch_watch_request(
1433 &mut touch_source_stream,
1434 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1435 )
1436 .await;
1437
1438 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1442 .await;
1443
1444 let events = read_uapi_events(&input_file, ¤t_task);
1446
1447 assert_eq!(events.len(), 6);
1448
1449 answer_next_touch_watch_request(
1451 &mut touch_source_stream,
1452 vec![make_touch_event_with_phase(EventPhase::Remove, 1)],
1453 )
1454 .await;
1455
1456 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1458 .await;
1459
1460 let events = read_uapi_events(&input_file, ¤t_task);
1461 assert_eq!(
1462 events,
1463 vec![
1464 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1465 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1466 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1467 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1468 ]
1469 );
1470 })
1471 .await;
1472 }
1473
1474 #[::fuchsia::test]
1475 async fn multi_touch_event_sequence() {
1476 spawn_kernel_and_run(async move |current_task| {
1477 let (_input_device, input_file, mut touch_source_stream) =
1480 start_touch_input(¤t_task).await;
1481
1482 answer_next_touch_watch_request(
1484 &mut touch_source_stream,
1485 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1486 )
1487 .await;
1488
1489 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1491 .await;
1492 let events = read_uapi_events(&input_file, ¤t_task);
1493
1494 assert_eq!(events.len(), 6);
1495
1496 answer_next_touch_watch_request(
1498 &mut touch_source_stream,
1499 vec![
1500 make_touch_event_with_coords(10.0, 20.0, 1),
1501 make_touch_event_with_phase(EventPhase::Add, 2),
1502 ],
1503 )
1504 .await;
1505
1506 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1508 .await;
1509 let events = read_uapi_events(&input_file, ¤t_task);
1510
1511 assert_eq!(
1512 events,
1513 vec![
1514 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1515 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1516 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1517 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1518 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1519 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1520 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1521 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1522 ]
1523 );
1524
1525 answer_next_touch_watch_request(
1527 &mut touch_source_stream,
1528 vec![
1529 make_touch_event_with_coords(11.0, 21.0, 1),
1530 make_touch_event_with_coords(101.0, 201.0, 2),
1531 ],
1532 )
1533 .await;
1534
1535 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1537 .await;
1538 let events = read_uapi_events(&input_file, ¤t_task);
1539
1540 assert_eq!(
1541 events,
1542 vec![
1543 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1544 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
1545 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
1546 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1547 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
1548 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
1549 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1550 ]
1551 );
1552
1553 answer_next_touch_watch_request(
1555 &mut touch_source_stream,
1556 vec![
1557 make_touch_event_with_phase(EventPhase::Remove, 1),
1558 make_touch_event_with_coords(102.0, 202.0, 2),
1559 ],
1560 )
1561 .await;
1562
1563 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1565 .await;
1566 let events = read_uapi_events(&input_file, ¤t_task);
1567
1568 assert_eq!(
1569 events,
1570 vec![
1571 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1572 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1573 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1574 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 102),
1575 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 202),
1576 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1577 ]
1578 );
1579
1580 answer_next_touch_watch_request(
1582 &mut touch_source_stream,
1583 vec![make_touch_event_with_phase(EventPhase::Remove, 2)],
1584 )
1585 .await;
1586
1587 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1589 .await;
1590 let events = read_uapi_events(&input_file, ¤t_task);
1591
1592 assert_eq!(
1593 events,
1594 vec![
1595 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1596 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1597 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1598 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1599 ]
1600 );
1601 })
1602 .await;
1603 }
1604
1605 #[::fuchsia::test]
1606 async fn multi_event_sequence_unsorted_in_one_watch() {
1607 spawn_kernel_and_run(async move |current_task| {
1608 let (_input_device, input_file, mut touch_source_stream) =
1611 start_touch_input(¤t_task).await;
1612
1613 answer_next_touch_watch_request(
1615 &mut touch_source_stream,
1616 vec![
1617 make_touch_event_with_coords_phase_timestamp(
1618 10.0,
1619 20.0,
1620 EventPhase::Change,
1621 1,
1622 100,
1623 ),
1624 make_touch_event_with_coords_phase_timestamp(0.0, 0.0, EventPhase::Add, 1, 1),
1625 ],
1626 )
1627 .await;
1628
1629 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1631 .await;
1632 let events = read_uapi_events(&input_file, ¤t_task);
1633
1634 assert_eq!(
1635 events,
1636 vec![
1637 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
1638 make_uapi_input_event_with_timestamp(
1639 uapi::EV_ABS,
1640 uapi::ABS_MT_TRACKING_ID,
1641 1,
1642 1
1643 ),
1644 make_uapi_input_event_with_timestamp(
1645 uapi::EV_ABS,
1646 uapi::ABS_MT_POSITION_X,
1647 0,
1648 1
1649 ),
1650 make_uapi_input_event_with_timestamp(
1651 uapi::EV_ABS,
1652 uapi::ABS_MT_POSITION_Y,
1653 0,
1654 1
1655 ),
1656 make_uapi_input_event_with_timestamp(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
1657 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
1658 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 100),
1659 make_uapi_input_event_with_timestamp(
1660 uapi::EV_ABS,
1661 uapi::ABS_MT_POSITION_X,
1662 10,
1663 100
1664 ),
1665 make_uapi_input_event_with_timestamp(
1666 uapi::EV_ABS,
1667 uapi::ABS_MT_POSITION_Y,
1668 20,
1669 100
1670 ),
1671 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 100),
1672 ]
1673 );
1674 })
1675 .await;
1676 }
1677
1678 #[test_case((0.0, 0.0); "origin")]
1679 #[test_case((100.7, 200.7); "above midpoint")]
1680 #[test_case((100.3, 200.3); "below midpoint")]
1681 #[test_case((100.5, 200.5); "midpoint")]
1682 #[::fuchsia::test]
1683 async fn sends_acceptable_coordinates((x, y): (f32, f32)) {
1684 spawn_kernel_and_run(async move |current_task| {
1685 let (_input_device, input_file, mut touch_source_stream) =
1687 start_touch_input(¤t_task).await;
1688
1689 answer_next_touch_watch_request(
1691 &mut touch_source_stream,
1692 vec![make_touch_event_with_coords_phase(x, y, EventPhase::Add, 1)],
1693 )
1694 .await;
1695
1696 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1698 .await;
1699 let events = read_uapi_events(&input_file, ¤t_task);
1700
1701 const ACCEPTABLE_ERROR: f32 = 1.0;
1704 let actual_x = events
1705 .iter()
1706 .find(|event| {
1707 event.type_ == uapi::EV_ABS as u16
1708 && event.code == uapi::ABS_MT_POSITION_X as u16
1709 })
1710 .unwrap_or_else(|| panic!("did not find `ABS_X` event in {:?}", events))
1711 .value;
1712 let actual_y = events
1713 .iter()
1714 .find(|event| {
1715 event.type_ == uapi::EV_ABS as u16
1716 && event.code == uapi::ABS_MT_POSITION_Y as u16
1717 })
1718 .unwrap_or_else(|| panic!("did not find `ABS_Y` event in {:?}", events))
1719 .value;
1720 assert_near!(x, actual_x as f32, ACCEPTABLE_ERROR);
1721 assert_near!(y, actual_y as f32, ACCEPTABLE_ERROR);
1722 })
1723 .await;
1724 }
1725
1726 #[::fuchsia::test]
1727 async fn sends_appropriate_ack_to_touch_source_server() {
1728 spawn_kernel_and_run(async move |current_task| {
1729 let (_input_device, _input_file, mut touch_source_stream) =
1730 start_touch_input(¤t_task).await;
1731
1732 let control_handle = touch_source_stream.control_handle();
1733 control_handle
1734 .send_on_touch_events(vec![make_touch_event_with_phase(EventPhase::Add, 2)], 42)
1735 .expect("failure sending OnTouchEvents");
1736 match touch_source_stream.next().await {
1737 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
1738 last_acknowledged_event_stamp,
1739 ..
1740 })) => assert_eq!(last_acknowledged_event_stamp, 42),
1741 unexpected => panic!("unexpected request {:?}", unexpected),
1742 }
1743 })
1744 .await;
1745 }
1746
1747 #[test_case(fidl_fuchsia_input::Key::Escape, uapi::KEY_POWER; "Esc maps to Power")]
1748 #[test_case(fidl_fuchsia_input::Key::A, uapi::KEY_A; "A maps to A")]
1749 #[::fuchsia::test]
1750 async fn sends_keyboard_events(fkey: fidl_fuchsia_input::Key, lkey: u32) {
1751 spawn_kernel_and_run(async move |current_task| {
1752 let (_keyboard_device, keyboard_file, keyboard_listener) =
1753 start_keyboard_input(¤t_task).await;
1754
1755 let key_event = fuiinput3::KeyEvent {
1756 timestamp: Some(0),
1757 type_: Some(fuiinput3::KeyEventType::Pressed),
1758 key: Some(fkey),
1759 ..Default::default()
1760 };
1761
1762 let _ = keyboard_listener.on_key_event(&key_event).await;
1763 std::mem::drop(keyboard_listener); let events = read_uapi_events(&keyboard_file, ¤t_task);
1765 assert_eq!(events.len(), 2);
1766 assert_eq!(events[0].code, lkey as u16);
1767 })
1768 .await;
1769 }
1770
1771 #[::fuchsia::test]
1772 async fn skips_unknown_keyboard_events() {
1773 spawn_kernel_and_run(async move |current_task| {
1774 let inspector = fuchsia_inspect::Inspector::default();
1775 let (_keyboard_device, keyboard_file, keyboard_listener) =
1776 start_keyboard_input_inspect(¤t_task, &inspector).await;
1777
1778 let key_event = fuiinput3::KeyEvent {
1779 timestamp: Some(0),
1780 type_: Some(fuiinput3::KeyEventType::Pressed),
1781 key: Some(fidl_fuchsia_input::Key::AcRefresh),
1782 ..Default::default()
1783 };
1784
1785 let _ = keyboard_listener.on_key_event(&key_event).await;
1786 std::mem::drop(keyboard_listener); let events = read_uapi_events(&keyboard_file, ¤t_task);
1788 assert_eq!(events.len(), 0);
1789
1790 assert_data_tree!(inspector, root: {
1793 keyboard_device: {
1794 active_wake_leases_count: AnyProperty,
1795 total_events_with_wake_lease_count: AnyProperty,
1796 total_fidl_events_received_count: 1u64,
1797 total_fidl_events_ignored_count: 1u64,
1798 total_fidl_events_unexpected_count: 0u64,
1799 total_fidl_events_converted_count: 0u64,
1800 total_uapi_events_generated_count: 0u64,
1801 last_generated_uapi_event_timestamp_ns: 0i64,
1802 keyboard_file_0: {
1803 fidl_events_received_count: 1u64,
1804 fidl_events_ignored_count: 1u64,
1805 fidl_events_unexpected_count: 0u64,
1806 fidl_events_converted_count: 0u64,
1807 uapi_events_generated_count: 0u64,
1808 uapi_events_read_count: 0u64,
1809 fd_read_count: AnyProperty,
1810 fd_notify_count: AnyProperty,
1811 last_generated_uapi_event_timestamp_ns: 0i64,
1812 last_read_uapi_event_timestamp_ns: AnyProperty,
1813 opened_without_nonblock: AnyProperty,
1814 open_timestamp_ns: AnyProperty,
1815 closed: AnyProperty,
1816 close_timestamp_ns: AnyProperty,
1817 }
1818 }
1819 });
1820 })
1821 .await;
1822 }
1823
1824 #[::fuchsia::test]
1825 async fn sends_power_button_events() {
1826 spawn_kernel_and_run(async move |current_task| {
1827 let (_input_device, input_file, buttons_listener) =
1828 start_button_input(¤t_task).await;
1829
1830 let power_event = MediaButtonsEvent {
1831 volume: Some(0),
1832 mic_mute: Some(false),
1833 pause: Some(false),
1834 camera_disable: Some(false),
1835 power: Some(true),
1836 function: Some(false),
1837 ..Default::default()
1838 };
1839
1840 let _ = buttons_listener.on_event(power_event).await;
1841 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1844 assert_eq!(events.len(), 2);
1845 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1846 assert_eq!(events[0].value, 1);
1847 })
1848 .await;
1849 }
1850
1851 #[::fuchsia::test]
1852 async fn sends_function_button_events() {
1853 spawn_kernel_and_run(async move |current_task| {
1854 let (_input_device, input_file, buttons_listener) =
1855 start_button_input(¤t_task).await;
1856
1857 let function_event = MediaButtonsEvent {
1858 volume: Some(0),
1859 mic_mute: Some(false),
1860 pause: Some(false),
1861 camera_disable: Some(false),
1862 power: Some(false),
1863 function: Some(true),
1864 ..Default::default()
1865 };
1866
1867 let _ = buttons_listener.on_event(function_event).await;
1868 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1871 assert_eq!(events.len(), 2);
1872 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
1873 assert_eq!(events[0].value, 1);
1874 })
1875 .await;
1876 }
1877
1878 #[::fuchsia::test]
1879 async fn sends_overlapping_button_events() {
1880 spawn_kernel_and_run(async move |current_task| {
1881 let (_input_device, input_file, buttons_listener) =
1882 start_button_input(¤t_task).await;
1883
1884 let power_event = MediaButtonsEvent {
1885 volume: Some(0),
1886 mic_mute: Some(false),
1887 pause: Some(false),
1888 camera_disable: Some(false),
1889 power: Some(true),
1890 function: Some(false),
1891 ..Default::default()
1892 };
1893
1894 let function_event = MediaButtonsEvent {
1895 volume: Some(0),
1896 mic_mute: Some(false),
1897 pause: Some(false),
1898 camera_disable: Some(false),
1899 power: Some(true),
1900 function: Some(true),
1901 ..Default::default()
1902 };
1903
1904 let function_release_event = MediaButtonsEvent {
1905 volume: Some(0),
1906 mic_mute: Some(false),
1907 pause: Some(false),
1908 camera_disable: Some(false),
1909 power: Some(true),
1910 function: Some(false),
1911 ..Default::default()
1912 };
1913
1914 let power_release_event = MediaButtonsEvent {
1915 volume: Some(0),
1916 mic_mute: Some(false),
1917 pause: Some(false),
1918 camera_disable: Some(false),
1919 power: Some(false),
1920 function: Some(false),
1921 ..Default::default()
1922 };
1923
1924 let _ = buttons_listener.on_event(power_event).await;
1925 let _ = buttons_listener.on_event(function_event).await;
1926 let _ = buttons_listener.on_event(function_release_event).await;
1927 let _ = buttons_listener.on_event(power_release_event).await;
1928 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1931 assert_eq!(events.len(), 8);
1932 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1933 assert_eq!(events[0].value, 1);
1934 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1935 assert_eq!(events[2].value, 1);
1936 assert_eq!(events[4].code, uapi::KEY_VOLUMEDOWN as u16);
1937 assert_eq!(events[4].value, 0);
1938 assert_eq!(events[6].code, uapi::KEY_POWER as u16);
1939 assert_eq!(events[6].value, 0);
1940 })
1941 .await;
1942 }
1943
1944 #[::fuchsia::test]
1945 async fn sends_simultaneous_button_events() {
1946 spawn_kernel_and_run(async move |current_task| {
1947 let (_input_device, input_file, buttons_listener) =
1948 start_button_input(¤t_task).await;
1949
1950 let power_and_function_event = MediaButtonsEvent {
1951 volume: Some(0),
1952 mic_mute: Some(false),
1953 pause: Some(false),
1954 camera_disable: Some(false),
1955 power: Some(true),
1956 function: Some(true),
1957 ..Default::default()
1958 };
1959
1960 let _ = buttons_listener.on_event(power_and_function_event).await;
1961 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1964 assert_eq!(events.len(), 4);
1965 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
1966 assert_eq!(events[0].value, 1);
1967 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
1968 assert_eq!(events[2].value, 1);
1969 })
1970 .await;
1971 }
1972
1973 #[::fuchsia::test]
1974 async fn sends_volume_up_button_events() {
1975 spawn_kernel_and_run(async move |current_task| {
1976 let (_input_device, input_file, buttons_listener) =
1977 start_button_input(¤t_task).await;
1978
1979 let volume_up_event = MediaButtonsEvent {
1980 volume: Some(1),
1981 mic_mute: Some(false),
1982 pause: Some(false),
1983 camera_disable: Some(false),
1984 power: Some(false),
1985 function: Some(false),
1986 ..Default::default()
1987 };
1988
1989 let _ = buttons_listener.on_event(volume_up_event).await;
1990 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1993 assert_eq!(events.len(), 2);
1994 assert_eq!(events[0].code, uapi::KEY_VOLUMEUP as u16);
1995 assert_eq!(events[0].value, 1);
1996 })
1997 .await;
1998 }
1999
2000 #[::fuchsia::test]
2001 async fn sends_volume_down_button_events() {
2002 spawn_kernel_and_run(async move |current_task| {
2003 let (_input_device, input_file, buttons_listener) =
2004 start_button_input(¤t_task).await;
2005
2006 let volume_down_event = MediaButtonsEvent {
2007 volume: Some(-1),
2008 mic_mute: Some(false),
2009 pause: Some(false),
2010 camera_disable: Some(false),
2011 power: Some(false),
2012 function: Some(false),
2013 ..Default::default()
2014 };
2015
2016 let _ = buttons_listener.on_event(volume_down_event).await;
2017 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
2020 assert_eq!(events.len(), 2);
2021 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
2022 assert_eq!(events[0].value, 1);
2023 })
2024 .await;
2025 }
2026
2027 #[test_case(1; "Scroll up")]
2028 #[test_case(-1; "Scroll down")]
2029 #[::fuchsia::test]
2030 async fn sends_mouse_wheel_events(ticks: i64) {
2031 spawn_kernel_and_run(async move |current_task| {
2032 let time = 100;
2033 let uapi_event = uapi::input_event {
2034 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time)),
2035 type_: uapi::EV_REL as u16,
2036 code: uapi::REL_WHEEL as u16,
2037 value: ticks as i32,
2038 };
2039
2040 let (_mouse_device, mouse_file, mut mouse_stream) =
2041 start_mouse_input(¤t_task).await;
2042
2043 answer_next_mouse_watch_request(
2044 &mut mouse_stream,
2045 vec![make_mouse_wheel_event_with_timestamp(ticks, time)],
2046 )
2047 .await;
2048
2049 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
2052
2053 let events = read_uapi_events(&mouse_file, ¤t_task);
2054 assert_eq!(events.len(), 2);
2055 assert_eq!(events[0], uapi_event);
2056 })
2057 .await;
2058 }
2059
2060 #[::fuchsia::test]
2061 async fn handles_mouse_move_and_click_events() {
2062 spawn_kernel_and_run(async move |current_task| {
2063 let mouse_move_event = fuipointer::MouseEvent {
2064 timestamp: Some(0),
2065 pointer_sample: Some(fuipointer::MousePointerSample {
2066 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2067 relative_motion: Some([10.0, 20.0]),
2068 ..Default::default()
2069 }),
2070 ..Default::default()
2071 };
2072 let mouse_click_event = fuipointer::MouseEvent {
2073 timestamp: Some(0),
2074 pointer_sample: Some(fuipointer::MousePointerSample {
2075 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2076 pressed_buttons: Some(vec![1]),
2077 ..Default::default()
2078 }),
2079 ..Default::default()
2080 };
2081
2082 let (_mouse_device, mouse_file, mut mouse_stream) =
2083 start_mouse_input(¤t_task).await;
2084
2085 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_move_event]).await;
2087 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_click_event]).await;
2088
2089 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
2092
2093 let events = read_uapi_events(&mouse_file, ¤t_task);
2094 assert!(!events.is_empty());
2095 assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
2096 && e.code == uapi::REL_X as u16
2097 && e.value == 10));
2098 assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
2099 && e.code == uapi::REL_Y as u16
2100 && e.value == 20));
2101 assert!(events.iter().any(|e| e.type_ == uapi::EV_KEY as u16
2102 && e.code == uapi::BTN_LEFT as u16
2103 && e.value == 1));
2104 })
2105 .await;
2106 }
2107
2108 #[::fuchsia::test]
2109 async fn touch_input_initialized_with_inspect_node() {
2110 spawn_kernel_and_run(async move |current_task| {
2111 let inspector = fuchsia_inspect::Inspector::default();
2112 let touch_device = InputDevice::new_touch(
2113 1200, 720, InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2116 "touch_device",
2117 &inspector.root(),
2118 );
2119 let _file_obj = touch_device.open_test(¤t_task);
2120
2121 assert_data_tree!(inspector, root: {
2122 touch_device: {
2123 active_wake_leases_count: 0u64,
2124 total_events_with_wake_lease_count: 0u64,
2125 total_fidl_events_received_count: 0u64,
2126 total_fidl_events_ignored_count: 0u64,
2127 total_fidl_events_unexpected_count: 0u64,
2128 total_fidl_events_converted_count: 0u64,
2129 total_uapi_events_generated_count: 0u64,
2130 last_generated_uapi_event_timestamp_ns: 0i64,
2131 touch_file_0: {
2132 fidl_events_received_count: 0u64,
2133 fidl_events_ignored_count: 0u64,
2134 fidl_events_unexpected_count: 0u64,
2135 fidl_events_converted_count: 0u64,
2136 uapi_events_generated_count: 0u64,
2137 uapi_events_read_count: 0u64,
2138 fd_read_count: 0u64,
2139 fd_notify_count: 0u64,
2140 last_generated_uapi_event_timestamp_ns: 0i64,
2141 last_read_uapi_event_timestamp_ns: 0i64,
2142 opened_without_nonblock: AnyProperty,
2143 open_timestamp_ns: AnyProperty,
2144 closed: AnyProperty,
2145 close_timestamp_ns: AnyProperty,
2146 }
2147 }
2148 });
2149 })
2150 .await;
2151 }
2152
2153 #[::fuchsia::test]
2154 async fn touch_relay_updates_touch_inspect_status() {
2155 spawn_kernel_and_run(async move |current_task| {
2156 let inspector = fuchsia_inspect::Inspector::default();
2157
2158 let (_input_device, input_file, mut touch_source_stream) =
2159 start_touch_input_inspect(¤t_task, &inspector).await;
2160
2161 let control_handle = touch_source_stream.control_handle();
2164 control_handle
2165 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2166 .expect("failure sending OnTouchEvents");
2167 match touch_source_stream.next().await {
2168 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2169 last_acknowledged_event_stamp,
2170 ..
2171 })) => assert_eq!(last_acknowledged_event_stamp, 2),
2172 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2173 }
2174
2175 control_handle
2178 .send_on_touch_events(
2179 vec![
2180 make_touch_event_with_coords_phase_timestamp(
2181 0.0,
2182 0.0,
2183 EventPhase::Add,
2184 1,
2185 1000,
2186 ),
2187 make_touch_event_with_coords_phase_timestamp(
2188 1.0,
2189 1.0,
2190 EventPhase::Change,
2191 1,
2192 2000,
2193 ),
2194 make_touch_event_with_coords_phase_timestamp(
2195 2.0,
2196 2.0,
2197 EventPhase::Change,
2198 1,
2199 3000,
2200 ),
2201 make_touch_event_with_coords_phase_timestamp(
2202 3.0,
2203 3.0,
2204 EventPhase::Change,
2205 1,
2206 4000,
2207 ),
2208 make_touch_event_with_coords_phase_timestamp(
2209 3.0,
2210 3.0,
2211 EventPhase::Remove,
2212 1,
2213 5000,
2214 ),
2215 ],
2216 7,
2217 )
2218 .expect("failure sending OnTouchEvents");
2219 match touch_source_stream.next().await {
2220 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2221 last_acknowledged_event_stamp,
2222 ..
2223 })) => assert_eq!(last_acknowledged_event_stamp, 7),
2224 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2225 }
2226
2227 let _events = read_uapi_events(&input_file, ¤t_task);
2228 assert_data_tree!(inspector, root: {
2229 touch_device: {
2230 active_wake_leases_count: 0u64,
2231 total_events_with_wake_lease_count: 0u64,
2232 total_fidl_events_received_count: 7u64,
2233 total_fidl_events_ignored_count: 2u64,
2234 total_fidl_events_unexpected_count: 0u64,
2235 total_fidl_events_converted_count: 5u64,
2236 total_uapi_events_generated_count: 22u64,
2237 last_generated_uapi_event_timestamp_ns: 5000i64,
2238 touch_file_0: {
2239 fidl_events_received_count: 7u64,
2240 fidl_events_ignored_count: 2u64,
2241 fidl_events_unexpected_count: 0u64,
2242 fidl_events_converted_count: 5u64,
2243 uapi_events_generated_count: 22u64,
2244 uapi_events_read_count: 22u64,
2245 fd_read_count: 23u64,
2246 fd_notify_count: 1u64,
2247 last_generated_uapi_event_timestamp_ns: 5000i64,
2248 last_read_uapi_event_timestamp_ns: 5000i64,
2249 opened_without_nonblock: AnyProperty,
2250 open_timestamp_ns: AnyProperty,
2251 closed: AnyProperty,
2252 close_timestamp_ns: AnyProperty,
2253 },
2254 }
2255 });
2256 })
2257 .await;
2258 }
2259
2260 #[::fuchsia::test]
2261 async fn new_file_updates_inspect_status() {
2262 spawn_kernel_and_run(async move |current_task| {
2263 let inspector = fuchsia_inspect::Inspector::default();
2264
2265 let input_device = InputDevice::new_touch(
2266 700,
2267 700,
2268 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2269 "touch_device",
2270 inspector.root(),
2271 );
2272 let input_file_0 =
2273 input_device.open_test(¤t_task).expect("Failed to create input file");
2274
2275 let (touch_source_client_end, mut touch_source_stream) =
2276 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
2277 let (mouse_source_client_end, _mouse_source_stream) =
2278 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
2279 let (keyboard_proxy, mut keyboard_stream) =
2280 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
2281 let view_ref_pair =
2282 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2283 let (device_registry_proxy, mut device_listener_stream) =
2284 fidl::endpoints::create_sync_proxy_and_stream::<
2285 fuipolicy::DeviceListenerRegistryMarker,
2286 >();
2287
2288 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
2289 relay.add_touch_device(
2290 input_event_relay::DEFAULT_TOUCH_DEVICE_ID,
2291 input_device.open_files.clone(),
2292 Some(input_device.inspect_status.clone()),
2293 );
2294 relay.start_relays(
2295 ¤t_task.kernel(),
2296 StartRelaysArgs {
2297 event_proxy_mode: EventProxyMode::None,
2298 touch_source_client_end,
2299 keyboard_proxy,
2300 mouse_source_client_end,
2301 view_ref: view_ref_pair.view_ref,
2302 registry_proxy: device_registry_proxy,
2303 device_listener_registry: None,
2304 display_width: 700,
2305 display_height: 700,
2306 },
2307 );
2308
2309 let _ = init_keyboard_listener(&mut keyboard_stream).await;
2310 let _ = init_button_listeners(&mut device_listener_stream).await;
2311
2312 let control_handle = touch_source_stream.control_handle();
2315 control_handle
2316 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2317 .expect("failure sending OnTouchEvents");
2318 match touch_source_stream.next().await {
2319 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2320 last_acknowledged_event_stamp,
2321 ..
2322 })) => assert_eq!(last_acknowledged_event_stamp, 2),
2323 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2324 }
2325
2326 input_device.open_files.lock().clear();
2328 drop(input_file_0);
2329
2330 let input_file_1 =
2332 input_device.open_test(¤t_task).expect("Failed to create input file");
2333
2334 control_handle
2337 .send_on_touch_events(
2338 vec![
2339 make_touch_event_with_coords_phase_timestamp(
2340 0.0,
2341 0.0,
2342 EventPhase::Add,
2343 1,
2344 1000,
2345 ),
2346 make_touch_event_with_coords_phase_timestamp(
2347 1.0,
2348 1.0,
2349 EventPhase::Change,
2350 1,
2351 2000,
2352 ),
2353 make_touch_event_with_coords_phase_timestamp(
2354 2.0,
2355 2.0,
2356 EventPhase::Change,
2357 1,
2358 3000,
2359 ),
2360 make_touch_event_with_coords_phase_timestamp(
2361 3.0,
2362 3.0,
2363 EventPhase::Change,
2364 1,
2365 4000,
2366 ),
2367 make_touch_event_with_coords_phase_timestamp(
2368 3.0,
2369 3.0,
2370 EventPhase::Remove,
2371 1,
2372 5000,
2373 ),
2374 ],
2375 7,
2376 )
2377 .expect("failure sending OnTouchEvents");
2378 match touch_source_stream.next().await {
2379 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2380 last_acknowledged_event_stamp,
2381 ..
2382 })) => assert_eq!(last_acknowledged_event_stamp, 7),
2383 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2384 }
2385
2386 let _events = read_uapi_events(&input_file_1, ¤t_task);
2387
2388 input_device.open_files.lock().clear();
2390 drop(input_file_1);
2391
2392 assert_data_tree!(inspector, root: {
2393 touch_device: {
2394 active_wake_leases_count: 0u64,
2395 total_events_with_wake_lease_count: 0u64,
2396 total_fidl_events_received_count: 7u64,
2397 total_fidl_events_ignored_count: 2u64,
2398 total_fidl_events_unexpected_count: 0u64,
2399 total_fidl_events_converted_count: 5u64,
2400 total_uapi_events_generated_count: 22u64,
2401 last_generated_uapi_event_timestamp_ns: 5000i64,
2402 touch_file_0: {
2403 fidl_events_received_count: 2u64,
2404 fidl_events_ignored_count: 2u64,
2405 fidl_events_unexpected_count: 0u64,
2406 fidl_events_converted_count: 0u64,
2407 uapi_events_generated_count: 0u64,
2408 uapi_events_read_count: 0u64,
2409 fd_read_count: 0u64,
2410 fd_notify_count: 0u64,
2411 last_generated_uapi_event_timestamp_ns: 0i64,
2412 last_read_uapi_event_timestamp_ns: 0i64,
2413 opened_without_nonblock: AnyProperty,
2414 open_timestamp_ns: AnyProperty,
2415 closed: AnyProperty,
2416 close_timestamp_ns: AnyProperty,
2417 },
2418 touch_file_1: {
2419 fidl_events_received_count: 5u64,
2420 fidl_events_ignored_count: 0u64,
2421 fidl_events_unexpected_count: 0u64,
2422 fidl_events_converted_count: 5u64,
2423 uapi_events_generated_count: 22u64,
2424 uapi_events_read_count: 22u64,
2425 fd_read_count: 23u64,
2426 fd_notify_count: 1u64,
2427 last_generated_uapi_event_timestamp_ns: 5000i64,
2428 last_read_uapi_event_timestamp_ns: 5000i64,
2429 opened_without_nonblock: AnyProperty,
2430 open_timestamp_ns: AnyProperty,
2431 closed: AnyProperty,
2432 close_timestamp_ns: AnyProperty,
2433 },
2434 }
2435 });
2436 })
2437 .await;
2438 }
2439
2440 #[::fuchsia::test]
2441 async fn file_status_inspect_not_empty_after_close() {
2442 spawn_kernel_and_run(async move |current_task| {
2443 let inspector = fuchsia_inspect::Inspector::default();
2444
2445 let input_device = InputDevice::new_touch(
2446 700,
2447 700,
2448 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2449 "touch_device",
2450 inspector.root(),
2451 );
2452 let file_handle =
2453 input_device.open_test(¤t_task).expect("Failed to create input file");
2454
2455 let status = file_handle
2456 .downcast_file::<crate::input_file::ArcInputFile>()
2457 .unwrap()
2458 .0
2459 .inspect_status
2460 .clone()
2461 .expect("touch file must have status");
2462 status.count_received_events(5);
2463
2464 assert_data_tree!(inspector, root: {
2465 touch_device: {
2466 active_wake_leases_count: AnyProperty,
2467 last_generated_uapi_event_timestamp_ns: AnyProperty,
2468 total_events_with_wake_lease_count: AnyProperty,
2469 total_fidl_events_converted_count: AnyProperty,
2470 total_fidl_events_ignored_count: AnyProperty,
2471 total_fidl_events_received_count: AnyProperty,
2472 total_fidl_events_unexpected_count: AnyProperty,
2473 total_uapi_events_generated_count: AnyProperty,
2474 touch_file_0: {
2475 fidl_events_received_count: 5u64,
2476 fd_notify_count: 0u64,
2477 fd_read_count: 0u64,
2478 fidl_events_converted_count: 0u64,
2479 fidl_events_ignored_count: 0u64,
2480 fidl_events_unexpected_count: 0u64,
2481 last_generated_uapi_event_timestamp_ns: 0i64,
2482 last_read_uapi_event_timestamp_ns: 0i64,
2483 uapi_events_generated_count: 0u64,
2484 uapi_events_read_count: 0u64,
2485 opened_without_nonblock: true,
2486 open_timestamp_ns: AnyProperty,
2487 closed: false,
2488 close_timestamp_ns: 0i64,
2489 }
2490 }
2491 });
2492
2493 drop(status);
2494 input_device.open_files.lock().clear();
2495 drop(file_handle);
2496
2497 assert_data_tree!(inspector, root: {
2498 touch_device: {
2499 active_wake_leases_count: AnyProperty,
2500 last_generated_uapi_event_timestamp_ns: AnyProperty,
2501 total_events_with_wake_lease_count: AnyProperty,
2502 total_fidl_events_converted_count: AnyProperty,
2503 total_fidl_events_ignored_count: AnyProperty,
2504 total_fidl_events_received_count: AnyProperty,
2505 total_fidl_events_unexpected_count: AnyProperty,
2506 total_uapi_events_generated_count: AnyProperty,
2507 touch_file_0: {
2508 fidl_events_received_count: 5u64,
2509 fd_notify_count: 0u64,
2510 fd_read_count: 0u64,
2511 fidl_events_converted_count: 0u64,
2512 fidl_events_ignored_count: 0u64,
2513 fidl_events_unexpected_count: 0u64,
2514 last_generated_uapi_event_timestamp_ns: 0i64,
2515 last_read_uapi_event_timestamp_ns: 0i64,
2516 uapi_events_generated_count: 0u64,
2517 uapi_events_read_count: 0u64,
2518 opened_without_nonblock: true,
2519 open_timestamp_ns: AnyProperty,
2520 closed: AnyProperty,
2521 close_timestamp_ns: AnyProperty,
2522 }
2523 }
2524 });
2525 })
2526 .await;
2527 }
2528
2529 #[::fuchsia::test]
2530 async fn keyboard_input_initialized_with_inspect_node() {
2531 spawn_kernel_and_run(async move |current_task| {
2532 let inspector = fuchsia_inspect::Inspector::default();
2533 let keyboard_device = InputDevice::new_keyboard(
2534 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
2535 "keyboard_device",
2536 &inspector.root(),
2537 );
2538 let _file_obj = keyboard_device.open_test(¤t_task);
2539
2540 assert_data_tree!(inspector, root: {
2541 keyboard_device: {
2542 active_wake_leases_count: 0u64,
2543 total_events_with_wake_lease_count: 0u64,
2544 total_fidl_events_received_count: 0u64,
2545 total_fidl_events_ignored_count: 0u64,
2546 total_fidl_events_unexpected_count: 0u64,
2547 total_fidl_events_converted_count: 0u64,
2548 total_uapi_events_generated_count: 0u64,
2549 last_generated_uapi_event_timestamp_ns: 0i64,
2550 keyboard_file_0: {
2551 fidl_events_received_count: 0u64,
2552 fidl_events_ignored_count: 0u64,
2553 fidl_events_unexpected_count: 0u64,
2554 fidl_events_converted_count: 0u64,
2555 uapi_events_generated_count: 0u64,
2556 uapi_events_read_count: 0u64,
2557 fd_read_count: 0u64,
2558 fd_notify_count: 0u64,
2559 last_generated_uapi_event_timestamp_ns: 0i64,
2560 last_read_uapi_event_timestamp_ns: 0i64,
2561 opened_without_nonblock: AnyProperty,
2562 open_timestamp_ns: AnyProperty,
2563 closed: AnyProperty,
2564 close_timestamp_ns: AnyProperty,
2565 }
2566 }
2567 });
2568 })
2569 .await;
2570 }
2571
2572 #[::fuchsia::test]
2573 async fn button_relay_updates_keyboard_inspect_status() {
2574 spawn_kernel_and_run(async move |current_task| {
2575 let inspector = fuchsia_inspect::Inspector::default();
2576
2577 let (_input_device, input_file, buttons_listener) =
2578 start_button_input_inspect(¤t_task, &inspector).await;
2579
2580 let power_event = MediaButtonsEvent {
2583 volume: Some(0),
2584 mic_mute: Some(false),
2585 pause: Some(false),
2586 camera_disable: Some(false),
2587 power: Some(true),
2588 function: Some(false),
2589 ..Default::default()
2590 };
2591
2592 let power_release_event = MediaButtonsEvent {
2593 volume: Some(0),
2594 mic_mute: Some(false),
2595 pause: Some(false),
2596 camera_disable: Some(false),
2597 power: Some(false),
2598 function: Some(false),
2599 ..Default::default()
2600 };
2601
2602 let _ = buttons_listener.on_event(power_event).await;
2603 let _ = buttons_listener.on_event(power_release_event).await;
2604
2605 let events = read_uapi_events(&input_file, ¤t_task);
2606 assert_eq!(events.len(), 4);
2607 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2608 assert_eq!(events[0].value, 1);
2609 assert_eq!(events[2].code, uapi::KEY_POWER as u16);
2610 assert_eq!(events[2].value, 0);
2611
2612 let _events = read_uapi_events(&input_file, ¤t_task);
2613
2614 assert_data_tree!(inspector, root: {
2615 keyboard_device: {
2616 active_wake_leases_count: 0u64,
2617 total_events_with_wake_lease_count: 0u64,
2618 total_fidl_events_received_count: 2u64,
2619 total_fidl_events_ignored_count: 0u64,
2620 total_fidl_events_unexpected_count: 0u64,
2621 total_fidl_events_converted_count: 2u64,
2622 total_uapi_events_generated_count: 4u64,
2623 last_generated_uapi_event_timestamp_ns: AnyProperty,
2625 keyboard_file_0: {
2626 fidl_events_received_count: 2u64,
2627 fidl_events_ignored_count: 0u64,
2628 fidl_events_unexpected_count: 0u64,
2629 fidl_events_converted_count: 2u64,
2630 uapi_events_generated_count: 4u64,
2631 uapi_events_read_count: 4u64,
2632 fd_read_count: 6u64,
2633 fd_notify_count: 2u64,
2634 last_generated_uapi_event_timestamp_ns: AnyProperty,
2636 last_read_uapi_event_timestamp_ns: AnyProperty,
2637 opened_without_nonblock: AnyProperty,
2638 open_timestamp_ns: AnyProperty,
2639 closed: AnyProperty,
2640 close_timestamp_ns: AnyProperty,
2641 },
2642 }
2643 });
2644 })
2645 .await;
2646 }
2647
2648 #[::fuchsia::test]
2649 async fn mouse_input_initialized_with_inspect_node() {
2650 spawn_kernel_and_run(async move |current_task| {
2651 let inspector = fuchsia_inspect::Inspector::default();
2652 let mouse_device = InputDevice::new_mouse(
2653 InputDeviceInfo::new(MOUSE_INPUT_ID, "starnix_mouse".to_string()),
2654 "mouse_device",
2655 &inspector.root(),
2656 );
2657 let _file_obj = mouse_device.open_test(¤t_task);
2658
2659 assert_data_tree!(inspector, root: {
2660 mouse_device: {
2661 active_wake_leases_count: 0u64,
2662 total_events_with_wake_lease_count: 0u64,
2663 total_fidl_events_received_count: 0u64,
2664 total_fidl_events_ignored_count: 0u64,
2665 total_fidl_events_unexpected_count: 0u64,
2666 total_fidl_events_converted_count: 0u64,
2667 total_uapi_events_generated_count: 0u64,
2668 last_generated_uapi_event_timestamp_ns: 0i64,
2669 mouse_file_0: {
2670 fidl_events_received_count: 0u64,
2671 fidl_events_ignored_count: 0u64,
2672 fidl_events_unexpected_count: 0u64,
2673 fidl_events_converted_count: 0u64,
2674 uapi_events_generated_count: 0u64,
2675 uapi_events_read_count: 0u64,
2676 fd_read_count: 0u64,
2677 fd_notify_count: 0u64,
2678 last_generated_uapi_event_timestamp_ns: 0i64,
2679 last_read_uapi_event_timestamp_ns: 0i64,
2680 opened_without_nonblock: AnyProperty,
2681 open_timestamp_ns: AnyProperty,
2682 closed: AnyProperty,
2683 close_timestamp_ns: AnyProperty,
2684 }
2685 }
2686 });
2687 })
2688 .await;
2689 }
2690
2691 #[::fuchsia::test]
2692 async fn mouse_relay_updates_mouse_inspect_status() {
2693 spawn_kernel_and_run(async move |current_task| {
2694 let inspector = fuchsia_inspect::Inspector::default();
2695
2696 let (_input_device, input_file, mut mouse_source_stream) =
2697 start_mouse_input_inspect(¤t_task, &inspector).await;
2698
2699 let mouse_move_event = fuipointer::MouseEvent {
2700 timestamp: Some(0),
2701 pointer_sample: Some(fuipointer::MousePointerSample {
2702 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2703 position_in_viewport: Some([50.0, 50.0]),
2704 scroll_v: Some(0),
2705 ..Default::default()
2706 }),
2707 ..Default::default()
2708 };
2709 let mouse_click_event = fuipointer::MouseEvent {
2710 timestamp: Some(0),
2711 pointer_sample: Some(fuipointer::MousePointerSample {
2712 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2713 scroll_v: Some(0),
2714 pressed_buttons: Some(vec![1]),
2715 ..Default::default()
2716 }),
2717 ..Default::default()
2718 };
2719
2720 answer_next_mouse_watch_request(
2723 &mut mouse_source_stream,
2724 vec![mouse_move_event, mouse_click_event],
2725 )
2726 .await;
2727
2728 answer_next_mouse_watch_request(
2731 &mut mouse_source_stream,
2732 (0..5).map(|_| make_mouse_wheel_event(1)).collect(),
2733 )
2734 .await;
2735
2736 answer_next_mouse_watch_request(
2739 &mut mouse_source_stream,
2740 vec![make_mouse_wheel_event_with_timestamp(-1, 5000)],
2741 )
2742 .await;
2743
2744 answer_next_mouse_watch_request(&mut mouse_source_stream, vec![]).await;
2747
2748 let _events = read_uapi_events(&input_file, ¤t_task);
2749 assert_data_tree!(inspector, root: {
2750 mouse_device: {
2751 active_wake_leases_count: 0u64,
2752 total_events_with_wake_lease_count: 0u64,
2753 total_fidl_events_received_count: 8u64,
2754 total_fidl_events_ignored_count: 1u64,
2755 total_fidl_events_unexpected_count: 0u64,
2756 total_fidl_events_converted_count: 7u64,
2757 total_uapi_events_generated_count: 7u64,
2758 last_generated_uapi_event_timestamp_ns: 5000i64,
2759 mouse_file_0: {
2760 fidl_events_received_count: 8u64,
2761 fidl_events_ignored_count: 1u64,
2762 fidl_events_unexpected_count: 0u64,
2763 fidl_events_converted_count: 7u64,
2764 uapi_events_generated_count: 7u64,
2765 uapi_events_read_count: 7u64,
2766 fd_read_count: 8u64,
2767 fd_notify_count: 3u64,
2768 last_generated_uapi_event_timestamp_ns: 5000i64,
2769 last_read_uapi_event_timestamp_ns: 5000i64,
2770 opened_without_nonblock: AnyProperty,
2771 open_timestamp_ns: AnyProperty,
2772 closed: AnyProperty,
2773 close_timestamp_ns: AnyProperty,
2774 },
2775 }
2776 });
2777 })
2778 .await;
2779 }
2780 #[::fuchsia::test]
2781 async fn eviocgname_zero_buffer_length_succeeds() {
2782 spawn_kernel_and_run(async move |current_task| {
2783 let inspector = fuchsia_inspect::Inspector::default();
2784 let touch_device = InputDevice::new_touch(
2785 700,
2786 1200,
2787 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2788 "touch_device",
2789 &inspector.root(),
2790 );
2791 let file =
2792 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2793
2794 let user_addr = starnix_core::testing::map_memory(
2795 ¤t_task,
2796 starnix_uapi::user_address::UserAddress::default(),
2797 4096,
2798 );
2799
2800 let res = file.ioctl(¤t_task, uapi::EVIOCGNAME_0, user_addr.into());
2802 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2803
2804 let name_req = uapi::EVIOCGNAME_0 | (256 << 16);
2806 let res = file.ioctl(¤t_task, name_req, user_addr.into());
2807 assert!(res.is_ok());
2808 let bytes_written = res.unwrap().value() as usize;
2809 assert!(bytes_written > 0);
2810 })
2811 .await;
2812 }
2813
2814 #[::fuchsia::test]
2815 async fn evdev_variable_length_ioctls_succeed() {
2816 spawn_kernel_and_run(async move |current_task| {
2817 let inspector = fuchsia_inspect::Inspector::default();
2818 let touch_device = InputDevice::new_touch(
2819 700,
2820 1200,
2821 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2822 "touch_device",
2823 &inspector.root(),
2824 );
2825 let file =
2826 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2827
2828 let user_addr = starnix_core::testing::map_memory(
2829 ¤t_task,
2830 starnix_uapi::user_address::UserAddress::default(),
2831 4096,
2832 );
2833
2834 let res = file.ioctl(¤t_task, uapi::EVIOCGRAB, 1u64.into());
2836 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2837
2838 let phys_req = crate::input_file::EVIOCGPHYS_BASE | (256 << 16);
2840 let res = file.ioctl(¤t_task, phys_req, user_addr.into());
2841 assert!(res.is_ok());
2842 let bytes_written = res.unwrap().value() as usize;
2843 assert!(bytes_written > 0);
2844
2845 let uniq_req = crate::input_file::EVIOCGUNIQ_BASE | (256 << 16);
2847 let res = file.ioctl(¤t_task, uniq_req, user_addr.into());
2848 assert_eq!(res, starnix_uapi::error!(ENOENT));
2849
2850 let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
2852 let res = file.ioctl(¤t_task, prop_req, user_addr.into());
2853 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2854
2855 let bit0_req = crate::input_file::EVIOCGBIT_0_BASE | (64 << 16);
2857 let res = file.ioctl(¤t_task, bit0_req, user_addr.into());
2858 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2859
2860 let snd_req = crate::input_file::EVIOCGBIT_EV_SND_BASE | (64 << 16);
2862 let res = file.ioctl(¤t_task, snd_req, user_addr.into());
2863 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2864
2865 let invalid_bit_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
2867 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2868 | (64 << 16)
2869 | (0x20 + 0x1f);
2870 let res = file.ioctl(¤t_task, invalid_bit_req, user_addr.into());
2871 assert_eq!(res, starnix_uapi::error!(EINVAL));
2872
2873 let unrecognized_evdev_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
2875 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2876 | 0xFF;
2877 let res = file.ioctl(¤t_task, unrecognized_evdev_req, user_addr.into());
2878 assert_eq!(res, starnix_uapi::error!(EINVAL));
2879
2880 let invalid_dir_req = (uapi::_IOC_WRITE << uapi::_IOC_DIRSHIFT)
2882 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
2883 | 0x20;
2884 let res = file.ioctl(¤t_task, invalid_dir_req, user_addr.into());
2885 assert_eq!(res, starnix_uapi::error!(EINVAL));
2886 })
2887 .await;
2888 }
2889
2890 #[::fuchsia::test]
2896 async fn evdev_current_state_queries_succeed() {
2897 spawn_kernel_and_run(async move |current_task| {
2898 let inspector = fuchsia_inspect::Inspector::default();
2899 let touch_device = InputDevice::new_touch(
2900 700,
2901 1200,
2902 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2903 "touch_device",
2904 &inspector.root(),
2905 );
2906 let file =
2907 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2908
2909 let user_addr = starnix_core::testing::map_memory(
2910 ¤t_task,
2911 starnix_uapi::user_address::UserAddress::default(),
2912 4096,
2913 );
2914
2915 const BUF_LEN: usize = 96;
2916 for (name, base) in [
2917 ("EVIOCGKEY", crate::input_file::EVIOCGKEY_BASE),
2918 ("EVIOCGLED", crate::input_file::EVIOCGLED_BASE),
2919 ("EVIOCGSND", crate::input_file::EVIOCGSND_BASE),
2920 ("EVIOCGSW", crate::input_file::EVIOCGSW_BASE),
2921 ] {
2922 current_task
2925 .write_memory(user_addr, &[0xAAu8; BUF_LEN])
2926 .expect("failed to poison buffer");
2927
2928 let req = base | ((BUF_LEN as u32) << 16);
2929 let res = file.ioctl(¤t_task, req, user_addr.into());
2930 assert_eq!(res, Ok(starnix_syscalls::SUCCESS), "{name} should succeed");
2931
2932 let read_back = current_task
2933 .read_memory_to_vec(user_addr, BUF_LEN)
2934 .expect("failed to read back buffer");
2935 assert_eq!(
2936 read_back,
2937 vec![0u8; BUF_LEN],
2938 "{name} should report nothing currently active"
2939 );
2940 }
2941 })
2942 .await;
2943 }
2944 #[::fuchsia::test]
2945 async fn keyboard_input_file_ioctls() {
2946 spawn_kernel_and_run(async move |current_task| {
2947 let inspector = fuchsia_inspect::Inspector::default();
2948 let keyboard_device = InputDevice::new_keyboard(
2949 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
2950 "keyboard_device",
2951 &inspector.root(),
2952 );
2953 let file = keyboard_device
2954 .open_test(¤t_task)
2955 .expect("Failed to open keyboard input file");
2956
2957 let user_addr = map_memory(¤t_task, UserAddress::default(), 4096);
2958
2959 let name_req = crate::input_file::EVIOCGNAME_BASE | (256 << 16);
2961 let res = file.ioctl(¤t_task, name_req, user_addr.into());
2962 assert!(res.is_ok());
2963
2964 let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
2966 let res = file.ioctl(¤t_task, prop_req, user_addr.into());
2967 assert_eq!(res, Ok(SUCCESS));
2968 let prop_buf = current_task.read_memory_to_vec(user_addr, 8).unwrap();
2969 let byte_idx = (uapi::INPUT_PROP_DIRECT / 8) as usize;
2970 let bit_idx = uapi::INPUT_PROP_DIRECT % 8;
2971 assert_ne!(prop_buf[byte_idx] & (1 << bit_idx), 0);
2972
2973 let key_req = crate::input_file::EVIOCGBIT_EV_KEY_BASE | (64 << 16);
2975 let res = file.ioctl(¤t_task, key_req, user_addr.into());
2976 assert_eq!(res, Ok(SUCCESS));
2977 let key_buf = current_task.read_memory_to_vec(user_addr, 64).unwrap();
2978
2979 let check_key = |key_code: u32| {
2980 let byte = (key_code / 8) as usize;
2981 let bit = key_code % 8;
2982 assert_ne!(
2983 key_buf[byte] & (1 << bit),
2984 0,
2985 "Keycode {} should be set in evdev bitmask",
2986 key_code
2987 );
2988 };
2989 check_key(uapi::KEY_A);
2990 check_key(uapi::KEY_Z);
2991 check_key(uapi::KEY_1);
2992 check_key(uapi::KEY_ENTER);
2993 check_key(uapi::KEY_SPACE);
2994 check_key(uapi::KEY_ESC);
2995 check_key(uapi::KEY_POWER);
2996 check_key(uapi::KEY_VOLUMEUP);
2997 check_key(uapi::KEY_VOLUMEDOWN);
2998
2999 let check_key_absent = |key_code: u32, name: &str| {
3004 let byte = (key_code / 8) as usize;
3005 let bit = key_code % 8;
3006 assert_eq!(
3007 key_buf[byte] & (1 << bit),
3008 0,
3009 "{} ({}) should NOT be set in the keyboard evdev bitmask",
3010 name,
3011 key_code
3012 );
3013 };
3014 check_key_absent(uapi::BTN_LEFT, "BTN_LEFT");
3015 check_key_absent(uapi::BTN_TRIGGER, "BTN_TRIGGER");
3016 check_key_absent(uapi::BTN_SOUTH, "BTN_SOUTH");
3017 check_key_absent(uapi::BTN_THUMBR, "BTN_THUMBR");
3018 })
3019 .await;
3020 }
3021
3022 #[::fuchsia::test]
3023 async fn keyboard_events_update_inspect_counters() {
3024 spawn_kernel_and_run(async move |current_task| {
3025 let inspector = fuchsia_inspect::Inspector::default();
3026 let (_keyboard_device, keyboard_file, keyboard_listener) =
3027 start_keyboard_input_inspect(¤t_task, &inspector).await;
3028
3029 let key_event = fuiinput3::KeyEvent {
3030 timestamp: Some(12345),
3031 type_: Some(fuiinput3::KeyEventType::Pressed),
3032 key: Some(fidl_fuchsia_input::Key::A),
3033 ..Default::default()
3034 };
3035
3036 let _ = keyboard_listener.on_key_event(&key_event).await;
3037 std::mem::drop(keyboard_listener);
3038
3039 let events = read_uapi_events(&keyboard_file, ¤t_task);
3041 assert_eq!(events.len(), 2);
3042
3043 assert_data_tree!(inspector, root: {
3047 keyboard_device: {
3048 active_wake_leases_count: AnyProperty,
3049 total_events_with_wake_lease_count: AnyProperty,
3050 total_fidl_events_received_count: 1u64,
3051 total_fidl_events_ignored_count: 0u64,
3052 total_fidl_events_unexpected_count: 0u64,
3053 total_fidl_events_converted_count: 1u64,
3054 total_uapi_events_generated_count: 2u64,
3055 last_generated_uapi_event_timestamp_ns: 12345i64,
3056 keyboard_file_0: {
3057 fidl_events_received_count: 1u64,
3058 fidl_events_ignored_count: 0u64,
3059 fidl_events_unexpected_count: 0u64,
3060 fidl_events_converted_count: 1u64,
3061 uapi_events_generated_count: 2u64,
3062 uapi_events_read_count: 2u64,
3063 fd_read_count: AnyProperty,
3064 fd_notify_count: AnyProperty,
3065 last_generated_uapi_event_timestamp_ns: 12345i64,
3066 last_read_uapi_event_timestamp_ns: AnyProperty,
3067 opened_without_nonblock: AnyProperty,
3068 open_timestamp_ns: AnyProperty,
3069 closed: AnyProperty,
3070 close_timestamp_ns: AnyProperty,
3071 }
3072 }
3073 });
3074 })
3075 .await;
3076 }
3077
3078 async fn wait_for_device(kernel: &Kernel, devt: StarnixDeviceId, present: bool) {
3079 while kernel.device_registry.get_device(devt, DeviceMode::Char).is_ok() != present {
3080 fuchsia_async::Timer::new(
3081 fuchsia_async::MonotonicDuration::from_millis(10).after_now(),
3082 )
3083 .await;
3084 }
3085 }
3086
3087 async fn wait_for_device_listener_registered(
3088 inspector: &fuchsia_inspect::Inspector,
3089 expected: bool,
3090 ) {
3091 loop {
3092 let hierarchy = fuchsia_inspect::reader::read(inspector).await.unwrap();
3093 if let Some(relay_node) = hierarchy.get_child("input_events_relay") {
3094 if let Some(prop) = relay_node.get_property("device_listener_registered") {
3095 if prop.boolean() == Some(expected) {
3096 break;
3097 }
3098 }
3099 }
3100 fuchsia_async::Timer::new(
3101 fuchsia_async::MonotonicDuration::from_millis(10).after_now(),
3102 )
3103 .await;
3104 }
3105 }
3106
3107 async fn wait_for_num_devices(inspector: &fuchsia_inspect::Inspector, expected: u64) {
3108 loop {
3109 let hierarchy = fuchsia_inspect::reader::read(inspector).await.unwrap();
3110 if let Some(relay_node) = hierarchy.get_child("input_events_relay") {
3111 if let Some(prop) = relay_node.get_property("num_devices") {
3112 if prop.uint() == Some(expected) {
3113 break;
3114 }
3115 }
3116 }
3117 fuchsia_async::Timer::new(
3118 fuchsia_async::MonotonicDuration::from_millis(10).after_now(),
3119 )
3120 .await;
3121 }
3122 }
3123
3124 #[::fuchsia::test]
3125 async fn test_dynamic_device_registration() {
3126 spawn_kernel_and_run(async move |current_task| {
3127 let kernel = current_task.kernel();
3128
3129 let (touch_source_client_end, _touch_source_stream) =
3131 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
3132 let (mouse_source_client_end, _mouse_source_stream) =
3133 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
3134 let (keyboard_proxy, mut keyboard_stream) =
3135 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
3136 let view_ref_pair =
3137 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
3138 let (device_registry_proxy, mut device_listener_stream) =
3139 fidl::endpoints::create_sync_proxy_and_stream::<
3140 fuipolicy::DeviceListenerRegistryMarker,
3141 >();
3142
3143 let (device_listener_registry_proxy, mut device_listener_registry_stream) =
3145 fidl::endpoints::create_sync_proxy_and_stream::<
3146 fuiinput::DeviceListenerRegistryMarker,
3147 >();
3148
3149 let inspector = fuchsia_inspect::Inspector::default();
3150
3151 let (relay, relay_handle) = input_event_relay::new_input_relay();
3152 let relay = relay.with_inspect_node(inspector.root());
3153 relay.start_relays(
3154 &kernel,
3155 StartRelaysArgs {
3156 event_proxy_mode: EventProxyMode::None,
3157 touch_source_client_end,
3158 keyboard_proxy,
3159 mouse_source_client_end,
3160 view_ref: view_ref_pair.view_ref,
3161 registry_proxy: device_registry_proxy,
3162 device_listener_registry: Some(device_listener_registry_proxy),
3163 display_width: 700,
3164 display_height: 700,
3165 },
3166 );
3167
3168 let _ = init_keyboard_listener(&mut keyboard_stream).await;
3169 let _ = init_button_listeners(&mut device_listener_stream).await;
3170 let (device_listener_proxy, mut device_iterator_stream) =
3171 init_device_listener(&mut device_listener_registry_stream).await;
3172 drain_empty_device_iterator(&mut device_iterator_stream).await;
3173
3174 let device_id = 42;
3176 let prepopulated_input_id =
3177 uapi::input_id { bustype: 25, vendor: 26, product: 27, version: 28 };
3178 let prepopulated_name = "prepopulated_touch_device".to_string();
3179 relay_handle.update_uinput_device(
3180 device_id,
3181 prepopulated_input_id,
3182 prepopulated_name.clone(),
3183 );
3184
3185 let descriptor = fuiinput::DeviceDescriptor {
3186 touch: Some(fuiinput::TouchDescriptor::default()),
3187 device_information: Some(fuiinput::DeviceInformation {
3188 product_name: Some("test_touch_device".to_string()),
3189 ..Default::default()
3190 }),
3191 ..Default::default()
3192 };
3193 let event = fuiinput::DeviceEvent {
3194 action: Some(fuiinput::Action::Added),
3195 device_id: Some(device_id),
3196 descriptor: Some(descriptor.clone()),
3197 ..Default::default()
3198 };
3199
3200 device_listener_proxy
3201 .on_device_changed(&event)
3202 .expect("Failed to send dynamic device Added event");
3203
3204 let touch_devt = StarnixDeviceId::new(INPUT_MAJOR, 0);
3206 wait_for_device(kernel, touch_devt, true).await;
3207
3208 let info = relay_handle
3210 .device_infos
3211 .lock()
3212 .get(&device_id)
3213 .expect("device_info exists")
3214 .clone();
3215 assert_eq!(info.lock().input_id, prepopulated_input_id);
3216 assert_eq!(info.lock().name, prepopulated_name);
3217
3218 let buttons_device_id = 43;
3220 let descriptor_buttons = fuiinput::DeviceDescriptor {
3221 consumer_control: Some(fuiinput::ConsumerControlDescriptor::default()),
3222 device_information: Some(fuiinput::DeviceInformation {
3223 product_name: Some("test_buttons_device".to_string()),
3224 ..Default::default()
3225 }),
3226 ..Default::default()
3227 };
3228 let event_buttons = fuiinput::DeviceEvent {
3229 action: Some(fuiinput::Action::Added),
3230 device_id: Some(buttons_device_id),
3231 descriptor: Some(descriptor_buttons.clone()),
3232 ..Default::default()
3233 };
3234 device_listener_proxy
3235 .on_device_changed(&event_buttons)
3236 .expect("Failed to send dynamic ConsumerControl device Added event");
3237
3238 let buttons_devt = StarnixDeviceId::new(INPUT_MAJOR, 1);
3239 wait_for_device(kernel, buttons_devt, true).await;
3240
3241 let keyboard_device_id = 44;
3243 let descriptor_keyboard = fuiinput::DeviceDescriptor {
3244 keyboard: Some(fuiinput::KeyboardDescriptor::default()),
3245 device_information: Some(fuiinput::DeviceInformation {
3246 product_name: Some("test_keyboard_device".to_string()),
3247 ..Default::default()
3248 }),
3249 ..Default::default()
3250 };
3251 let event_keyboard = fuiinput::DeviceEvent {
3252 action: Some(fuiinput::Action::Added),
3253 device_id: Some(keyboard_device_id),
3254 descriptor: Some(descriptor_keyboard.clone()),
3255 ..Default::default()
3256 };
3257 device_listener_proxy
3258 .on_device_changed(&event_keyboard)
3259 .expect("Failed to send dynamic Keyboard device Added event");
3260
3261 let keyboard_devt = StarnixDeviceId::new(INPUT_MAJOR, 2);
3262 wait_for_device(kernel, keyboard_devt, true).await;
3263
3264 let mouse_device_id = 45;
3266 let descriptor_mouse = fuiinput::DeviceDescriptor {
3267 mouse: Some(fuiinput::MouseDescriptor::default()),
3268 device_information: Some(fuiinput::DeviceInformation {
3269 product_name: Some("test_mouse_device".to_string()),
3270 ..Default::default()
3271 }),
3272 ..Default::default()
3273 };
3274 let event_mouse = fuiinput::DeviceEvent {
3275 action: Some(fuiinput::Action::Added),
3276 device_id: Some(mouse_device_id),
3277 descriptor: Some(descriptor_mouse.clone()),
3278 ..Default::default()
3279 };
3280 device_listener_proxy
3281 .on_device_changed(&event_mouse)
3282 .expect("Failed to send dynamic Mouse device Added event");
3283
3284 let mouse_devt = StarnixDeviceId::new(INPUT_MAJOR, 3);
3285 wait_for_device(kernel, mouse_devt, true).await;
3286
3287 wait_for_num_devices(&inspector, 4).await;
3288 assert_data_tree!(inspector, root: contains {
3289 input_events_relay: contains {
3290 num_devices: 4u64,
3291 device_listener_registered: true,
3292 }
3293 });
3294
3295 let event_removed = fuiinput::DeviceEvent {
3297 action: Some(fuiinput::Action::Removed),
3298 device_id: Some(device_id),
3299 descriptor: Some(descriptor.clone()),
3300 ..Default::default()
3301 };
3302 device_listener_proxy
3303 .on_device_changed(&event_removed)
3304 .expect("Failed to send dynamic device Removed event");
3305
3306 wait_for_device(kernel, touch_devt, false).await;
3308 assert!(relay_handle.device_infos.lock().get(&device_id).is_none());
3309
3310 let replacement_device_id = 46;
3312 let replacement_descriptor = fuiinput::DeviceDescriptor {
3313 touch: Some(fuiinput::TouchDescriptor::default()),
3314 device_information: Some(fuiinput::DeviceInformation {
3315 product_name: Some("test_replacement_touch_device".to_string()),
3316 ..Default::default()
3317 }),
3318 ..Default::default()
3319 };
3320 let event_readd = fuiinput::DeviceEvent {
3321 action: Some(fuiinput::Action::Added),
3322 device_id: Some(replacement_device_id),
3323 descriptor: Some(replacement_descriptor.clone()),
3324 ..Default::default()
3325 };
3326 device_listener_proxy
3327 .on_device_changed(&event_readd)
3328 .expect("Failed to send dynamic device Added event for replacement device");
3329
3330 wait_for_device(kernel, touch_devt, true).await;
3332
3333 let event_replacement_removed = fuiinput::DeviceEvent {
3335 action: Some(fuiinput::Action::Removed),
3336 device_id: Some(replacement_device_id),
3337 descriptor: Some(replacement_descriptor.clone()),
3338 ..Default::default()
3339 };
3340 device_listener_proxy
3341 .on_device_changed(&event_replacement_removed)
3342 .expect("Failed to send dynamic device Removed event for replacement device");
3343
3344 wait_for_device(kernel, touch_devt, false).await;
3345
3346 let event_keyboard_removed = fuiinput::DeviceEvent {
3348 action: Some(fuiinput::Action::Removed),
3349 device_id: Some(keyboard_device_id),
3350 descriptor: Some(descriptor_keyboard.clone()),
3351 ..Default::default()
3352 };
3353 device_listener_proxy
3354 .on_device_changed(&event_keyboard_removed)
3355 .expect("Failed to send dynamic Keyboard device Removed event");
3356
3357 wait_for_device(kernel, keyboard_devt, false).await;
3358
3359 let event_mouse_removed = fuiinput::DeviceEvent {
3361 action: Some(fuiinput::Action::Removed),
3362 device_id: Some(mouse_device_id),
3363 descriptor: Some(descriptor_mouse.clone()),
3364 ..Default::default()
3365 };
3366 device_listener_proxy
3367 .on_device_changed(&event_mouse_removed)
3368 .expect("Failed to send dynamic Mouse device Removed event");
3369
3370 wait_for_device(kernel, mouse_devt, false).await;
3371
3372 wait_for_num_devices(&inspector, 1).await;
3373 assert_data_tree!(inspector, root: contains {
3374 input_events_relay: contains {
3375 num_devices: 1u64,
3376 device_listener_registered: true,
3377 }
3378 });
3379
3380 drop(device_listener_proxy);
3383 wait_for_device_listener_registered(&inspector, false).await;
3384 })
3385 .await;
3386 }
3387
3388 #[::fuchsia::test]
3389 async fn test_existing_devices_iterator() {
3390 spawn_kernel_and_run(async move |current_task| {
3391 let kernel = current_task.kernel();
3392
3393 let (touch_source_client_end, _touch_source_stream) =
3395 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
3396 let (mouse_source_client_end, _mouse_source_stream) =
3397 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
3398 let (keyboard_proxy, mut keyboard_stream) =
3399 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
3400 let view_ref_pair =
3401 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
3402 let (device_registry_proxy, mut device_listener_stream) =
3403 fidl::endpoints::create_sync_proxy_and_stream::<
3404 fuipolicy::DeviceListenerRegistryMarker,
3405 >();
3406
3407 let (device_listener_registry_proxy, mut device_listener_registry_stream) =
3409 fidl::endpoints::create_sync_proxy_and_stream::<
3410 fuiinput::DeviceListenerRegistryMarker,
3411 >();
3412
3413 let inspector = fuchsia_inspect::Inspector::default();
3414
3415 let (relay, _relay_handle) = input_event_relay::new_input_relay();
3417 let relay = relay.with_inspect_node(inspector.root());
3418 relay.start_relays(
3419 &kernel,
3420 StartRelaysArgs {
3421 event_proxy_mode: EventProxyMode::None,
3422 touch_source_client_end,
3423 keyboard_proxy,
3424 mouse_source_client_end,
3425 view_ref: view_ref_pair.view_ref,
3426 registry_proxy: device_registry_proxy,
3427 device_listener_registry: Some(device_listener_registry_proxy),
3428 display_width: 700,
3429 display_height: 700,
3430 },
3431 );
3432
3433 let _ = init_keyboard_listener(&mut keyboard_stream).await;
3435 let _ = init_button_listeners(&mut device_listener_stream).await;
3436 let (_device_listener_proxy, mut device_iterator_stream) =
3437 init_device_listener(&mut device_listener_registry_stream).await;
3438
3439 let touch_event = fuiinput::DeviceEvent {
3442 action: Some(fuiinput::Action::Added),
3443 device_id: Some(10),
3444 descriptor: Some(fuiinput::DeviceDescriptor {
3445 touch: Some(fuiinput::TouchDescriptor::default()),
3446 device_information: Some(fuiinput::DeviceInformation {
3447 product_name: Some("existing_touch_device".to_string()),
3448 ..Default::default()
3449 }),
3450 ..Default::default()
3451 }),
3452 ..Default::default()
3453 };
3454 let keyboard_event = fuiinput::DeviceEvent {
3455 action: Some(fuiinput::Action::Added),
3456 device_id: Some(11),
3457 descriptor: Some(fuiinput::DeviceDescriptor {
3458 keyboard: Some(fuiinput::KeyboardDescriptor::default()),
3459 device_information: Some(fuiinput::DeviceInformation {
3460 product_name: Some("existing_keyboard_device".to_string()),
3461 ..Default::default()
3462 }),
3463 ..Default::default()
3464 }),
3465 ..Default::default()
3466 };
3467
3468 match device_iterator_stream.next().await {
3469 Some(Ok(fuiinput::DeviceIteratorRequest::GetNext { responder })) => {
3470 responder
3471 .send(&[touch_event, keyboard_event])
3472 .expect("failed to send first batch");
3473 }
3474 other => panic!("Unexpected request on DeviceIterator: {:?}", other),
3475 }
3476
3477 match device_iterator_stream.next().await {
3479 Some(Ok(fuiinput::DeviceIteratorRequest::GetNext { responder })) => {
3480 responder.send(&[]).expect("failed to send empty batch");
3481 }
3482 other => panic!("Unexpected request on DeviceIterator: {:?}", other),
3483 }
3484
3485 let touch_devt = StarnixDeviceId::new(INPUT_MAJOR, 0);
3487 wait_for_device(kernel, touch_devt, true).await;
3488
3489 let keyboard_devt = StarnixDeviceId::new(INPUT_MAJOR, 1);
3490 wait_for_device(kernel, keyboard_devt, true).await;
3491
3492 wait_for_num_devices(&inspector, 2).await;
3493 assert_data_tree!(inspector, root: contains {
3494 input_events_relay: contains {
3495 num_devices: 2u64,
3496 device_listener_registered: true,
3497 }
3498 });
3499 })
3500 .await;
3501 }
3502
3503 #[::fuchsia::test]
3504 async fn test_dynamic_device_registration_failure() {
3505 spawn_kernel_and_run(async move |current_task| {
3506 let kernel = current_task.kernel();
3507
3508 let (touch_source_client_end, _touch_source_stream) =
3510 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
3511 let (mouse_source_client_end, _mouse_source_stream) =
3512 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
3513 let (keyboard_proxy, mut keyboard_stream) =
3514 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
3515 let view_ref_pair =
3516 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
3517 let (device_registry_proxy, mut device_listener_stream) =
3518 fidl::endpoints::create_sync_proxy_and_stream::<
3519 fuipolicy::DeviceListenerRegistryMarker,
3520 >();
3521
3522 let (device_listener_registry_proxy, device_listener_registry_stream) =
3525 fidl::endpoints::create_sync_proxy_and_stream::<
3526 fuiinput::DeviceListenerRegistryMarker,
3527 >();
3528 drop(device_listener_registry_stream);
3529
3530 let inspector = fuchsia_inspect::Inspector::default();
3531
3532 let (relay, _relay_handle) = input_event_relay::new_input_relay();
3534 let relay = relay.with_inspect_node(inspector.root());
3535 relay.start_relays(
3536 &kernel,
3537 StartRelaysArgs {
3538 event_proxy_mode: EventProxyMode::None,
3539 touch_source_client_end,
3540 keyboard_proxy,
3541 mouse_source_client_end,
3542 view_ref: view_ref_pair.view_ref,
3543 registry_proxy: device_registry_proxy,
3544 device_listener_registry: Some(device_listener_registry_proxy),
3545 display_width: 700,
3546 display_height: 700,
3547 },
3548 );
3549
3550 let keyboard_listener = init_keyboard_listener(&mut keyboard_stream).await;
3551 let _ = init_button_listeners(&mut device_listener_stream).await;
3552
3553 let key_event = fuiinput3::KeyEvent {
3557 timestamp: Some(0),
3558 type_: Some(fuiinput3::KeyEventType::Pressed),
3559 key: Some(fidl_fuchsia_input::Key::A),
3560 ..Default::default()
3561 };
3562 let _ = keyboard_listener.on_key_event(&key_event).await;
3563
3564 wait_for_device_listener_registered(&inspector, false).await;
3565
3566 assert_data_tree!(inspector, root: contains {
3567 input_events_relay: contains {
3568 num_devices: 0u64,
3569 device_listener_registered: false,
3570 }
3571 });
3572 })
3573 .await;
3574 }
3575
3576 #[::fuchsia::test]
3577 async fn register_nests_event_device_under_input_device() {
3578 spawn_kernel_and_run(async move |current_task| {
3579 let kernel = current_task.kernel();
3580 let inspector = fuchsia_inspect::Inspector::default();
3581 let info = InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string());
3582 let keyboard_device =
3583 InputDevice::new_keyboard(info.clone(), "keyboard_device", &inspector.root());
3584
3585 let event_device =
3586 keyboard_device.register(kernel, 7).expect("failed to register input device");
3587 assert_eq!(event_device.name(), "event7");
3588 assert_eq!(event_device.parent().map(|parent| parent.name()), Some("input7".into()));
3589
3590 let root = &kernel.device_registry.objects.root;
3591 for path in [
3592 "devices/virtual/input/input7/uevent",
3593 "devices/virtual/input/input7/event7/dev",
3594 "devices/virtual/input/input7/event7/uevent",
3595 "class/input/input7",
3596 "class/input/event7",
3597 "dev/char/13:7",
3598 ] {
3599 assert!(root.lookup(path.into()).is_some(), "missing {path}");
3600 }
3601
3602 let read_attr = |path: &str| -> String {
3603 let node = root.lookup(path.into()).unwrap_or_else(|| panic!("missing {path}"));
3604 let file_ops =
3605 node.create_file_ops(¤t_task, OpenFlags::RDONLY).expect("open attr");
3606 let file = starnix_core::testing::anon_test_file(
3607 ¤t_task,
3608 file_ops,
3609 OpenFlags::RDONLY,
3610 );
3611 let mut buf = VecOutputBuffer::new(64);
3612 let bytes_read = file.read(¤t_task, &mut buf).expect("read attr");
3613 String::from_utf8(Vec::from(&buf.data()[..bytes_read])).expect("utf8 attr")
3614 };
3615 assert_eq!(read_attr("devices/virtual/input/input7/name"), "starnix_buttons\n");
3616 assert_eq!(
3617 read_attr("devices/virtual/input/input7/id/bustype"),
3618 format!("{:04x}\n", KEYBOARD_INPUT_ID.bustype)
3619 );
3620 assert_eq!(
3621 read_attr("devices/virtual/input/input7/id/vendor"),
3622 format!("{:04x}\n", KEYBOARD_INPUT_ID.vendor)
3623 );
3624 assert_eq!(
3625 read_attr("devices/virtual/input/input7/id/product"),
3626 format!("{:04x}\n", KEYBOARD_INPUT_ID.product)
3627 );
3628 assert_eq!(
3629 read_attr("devices/virtual/input/input7/id/version"),
3630 format!("{:04x}\n", KEYBOARD_INPUT_ID.version)
3631 );
3632
3633 *info.lock() = InputDeviceInfo {
3635 input_id: input_id { bustype: 0x0003, vendor: 0x1234, product: 0x5678, version: 9 },
3636 name: "custom_uinput".to_string(),
3637 };
3638 assert_eq!(read_attr("devices/virtual/input/input7/name"), "custom_uinput\n");
3639 assert_eq!(read_attr("devices/virtual/input/input7/id/bustype"), "0003\n");
3640 assert_eq!(read_attr("devices/virtual/input/input7/id/vendor"), "1234\n");
3641 assert_eq!(read_attr("devices/virtual/input/input7/id/product"), "5678\n");
3642 assert_eq!(read_attr("devices/virtual/input/input7/id/version"), "0009\n");
3643
3644 assert!(root.lookup("devices/virtual/input/input7/input".into()).is_none());
3647
3648 InputDevice::unregister(¤t_task, event_device);
3649 for path in [
3650 "devices/virtual/input/input7",
3651 "class/input/input7",
3652 "class/input/event7",
3653 "dev/char/13:7",
3654 ] {
3655 assert!(root.lookup(path.into()).is_none(), "unexpected {path}");
3656 }
3657 })
3658 .await;
3659 }
3660}