1use crate::InputFile;
6use crate::input_event_relay::{
7 FUCHSIA_KEYBOARD_PRODUCT_ID, FUCHSIA_MOUSE_PRODUCT_ID, FUCHSIA_TOUCH_PRODUCT_ID,
8 FUCHSIA_VENDOR_ID, OpenedFiles,
9};
10use futures::FutureExt;
11use starnix_core::device::kobject::{Device, DeviceMetadata};
12use starnix_core::device::{DeviceMode, DeviceOps};
13use starnix_core::fs::sysfs::build_device_directory;
14use starnix_core::task::{CurrentTask, Kernel};
15use starnix_core::vfs::pseudo::simple_file::BytesFile;
16use starnix_core::vfs::{FileOps, NamespaceNode};
17use starnix_sync::{InputDeviceFileNodesLock, InputDeviceInfoLock, LockDepMutex};
18use starnix_uapi::device_id::{DeviceId as StarnixDeviceId, INPUT_MAJOR};
19use starnix_uapi::errors::Errno;
20use starnix_uapi::file_mode::mode;
21use starnix_uapi::open_flags::OpenFlags;
22use starnix_uapi::{input_id, uapi};
23use std::sync::Arc;
24use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
25
26#[derive(Clone)]
27enum InputDeviceId {
28 Touch(i32, i32),
30
31 Keyboard,
33
34 Mouse,
36
37 Descriptor(Arc<crate::input_file::InputFileConfig>),
39}
40
41pub struct InputDeviceStatus {
48 pub node: fuchsia_inspect::Node,
50
51 pub file_nodes: LockDepMutex<Vec<fuchsia_inspect::Node>, InputDeviceFileNodesLock>,
54
55 pub total_fidl_events_received_count: AtomicU64,
66
67 pub total_fidl_events_ignored_count: AtomicU64,
70
71 pub total_fidl_events_unexpected_count: AtomicU64,
75
76 pub total_fidl_events_converted_count: AtomicU64,
79
80 pub total_uapi_events_generated_count: AtomicU64,
82
83 pub last_generated_uapi_event_timestamp_ns: AtomicI64,
85
86 pub total_events_with_wake_lease_count: AtomicU64,
88
89 pub active_wake_leases_count: AtomicU64,
91}
92
93impl InputDeviceStatus {
94 pub fn new(node: fuchsia_inspect::Node) -> Arc<Self> {
95 let status = Arc::new(Self {
96 node,
97 file_nodes: Default::default(),
98 total_fidl_events_received_count: AtomicU64::new(0),
99 total_fidl_events_ignored_count: AtomicU64::new(0),
100 total_fidl_events_unexpected_count: AtomicU64::new(0),
101 total_fidl_events_converted_count: AtomicU64::new(0),
102 total_uapi_events_generated_count: AtomicU64::new(0),
103 last_generated_uapi_event_timestamp_ns: AtomicI64::new(0),
104 total_events_with_wake_lease_count: AtomicU64::new(0),
105 active_wake_leases_count: AtomicU64::new(0),
106 });
107
108 let weak_status = Arc::downgrade(&status);
109 status.node.record_lazy_values("status", move || {
110 let status = weak_status.upgrade();
111 async move {
112 let inspector = fuchsia_inspect::Inspector::default();
113 if let Some(status) = status {
114 let root = inspector.root();
115 root.record_uint(
116 "total_fidl_events_received_count",
117 status.total_fidl_events_received_count.load(Ordering::Relaxed),
118 );
119 root.record_uint(
120 "total_fidl_events_ignored_count",
121 status.total_fidl_events_ignored_count.load(Ordering::Relaxed),
122 );
123 root.record_uint(
124 "total_fidl_events_unexpected_count",
125 status.total_fidl_events_unexpected_count.load(Ordering::Relaxed),
126 );
127 root.record_uint(
128 "total_fidl_events_converted_count",
129 status.total_fidl_events_converted_count.load(Ordering::Relaxed),
130 );
131 root.record_uint(
132 "total_uapi_events_generated_count",
133 status.total_uapi_events_generated_count.load(Ordering::Relaxed),
134 );
135 root.record_int(
136 "last_generated_uapi_event_timestamp_ns",
137 status.last_generated_uapi_event_timestamp_ns.load(Ordering::Relaxed),
138 );
139 root.record_uint(
140 "total_events_with_wake_lease_count",
141 status.total_events_with_wake_lease_count.load(Ordering::Relaxed),
142 );
143 root.record_uint(
144 "active_wake_leases_count",
145 status.active_wake_leases_count.load(Ordering::Relaxed),
146 );
147 }
148 Ok(inspector)
149 }
150 .boxed()
151 });
152
153 status
154 }
155
156 pub fn count_total_received_events(&self, count: u64) {
157 self.total_fidl_events_received_count.fetch_add(count, Ordering::Relaxed);
158 }
159
160 pub fn count_events_with_wake_lease(&self, count: u64) {
161 self.total_events_with_wake_lease_count.fetch_add(count, Ordering::Relaxed);
162 }
163
164 pub fn increment_active_wake_leases(&self, count: u64) {
165 self.active_wake_leases_count.fetch_add(count, Ordering::Relaxed);
166 }
167
168 pub fn decrement_active_wake_leases(&self, count: u64) {
169 self.active_wake_leases_count.fetch_sub(count, Ordering::Relaxed);
170 }
171
172 pub fn count_total_ignored_events(&self, count: u64) {
173 self.total_fidl_events_ignored_count.fetch_add(count, Ordering::Relaxed);
174 }
175
176 pub fn count_total_unexpected_events(&self, count: u64) {
177 self.total_fidl_events_unexpected_count.fetch_add(count, Ordering::Relaxed);
178 }
179
180 pub fn count_total_converted_events(&self, count: u64) {
181 self.total_fidl_events_converted_count.fetch_add(count, Ordering::Relaxed);
182 }
183
184 pub fn count_total_generated_events(&self, count: u64, event_time_ns: i64) {
185 self.total_uapi_events_generated_count.fetch_add(count, Ordering::Relaxed);
186 self.last_generated_uapi_event_timestamp_ns.store(event_time_ns, Ordering::Relaxed);
187 }
188}
189
190#[derive(Clone, Debug, PartialEq)]
191pub struct InputDeviceInfo {
192 pub input_id: input_id,
193 pub name: String,
194}
195
196pub type InputDeviceInfoHandle = Arc<LockDepMutex<InputDeviceInfo, InputDeviceInfoLock>>;
197
198impl InputDeviceInfo {
199 pub fn new(input_id: input_id, name: String) -> InputDeviceInfoHandle {
200 Arc::new(LockDepMutex::new(Self { input_id, name }))
201 }
202}
203
204#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
206pub struct DeviceInfo {
207 pub vendor_id: u32,
208 pub product_id: u32,
209 pub version: u32,
210 pub bus_type: u16,
211}
212
213impl DeviceInfo {
214 pub fn new(bus_type: u16, vendor_id: u32, product_id: u32, version: u32) -> Self {
215 Self { bus_type, vendor_id, product_id, version }
216 }
217}
218
219pub fn parse_device_info(
222 descriptor: &fidl_fuchsia_input_report::DeviceDescriptor,
223 device_info: Option<DeviceInfo>,
224) -> InputDeviceInfoHandle {
225 let desc_info = descriptor.device_information.as_ref();
226 let mut resolved_vendor = desc_info.and_then(|i| i.vendor_id).unwrap_or(0);
227 let mut resolved_product = desc_info.and_then(|i| i.product_id).unwrap_or(0);
228 let mut resolved_version = desc_info.and_then(|i| i.version).unwrap_or(0);
229 let mut resolved_bus_type = 0;
230
231 if let Some(info) = device_info {
232 if (info.vendor_id as u16) != 0 {
233 resolved_vendor = info.vendor_id;
234 }
235 if (info.product_id as u16) != 0 {
236 resolved_product = info.product_id;
237 }
238 if (info.version as u16) != 0 {
239 resolved_version = info.version;
240 }
241 if info.bus_type != 0 {
242 resolved_bus_type = info.bus_type;
243 }
244 }
245
246 let mut vendor_16 = (resolved_vendor & 0xFFFF) as u16;
247 let mut product_16 = (resolved_product & 0xFFFF) as u16;
248 let mut version_16 = (resolved_version & 0xFFFF) as u16;
249
250 if resolved_bus_type == 0 {
251 resolved_bus_type = if vendor_16 != 0 || product_16 != 0 {
252 uapi::BUS_USB as u16
253 } else {
254 uapi::BUS_VIRTUAL as u16
255 };
256 }
257
258 if vendor_16 == 0 && product_16 == 0 {
259 if descriptor.touch.is_some() {
260 vendor_16 = FUCHSIA_VENDOR_ID;
261 product_16 = FUCHSIA_TOUCH_PRODUCT_ID;
262 } else if descriptor.keyboard.is_some() {
263 vendor_16 = FUCHSIA_VENDOR_ID;
264 product_16 = FUCHSIA_KEYBOARD_PRODUCT_ID;
265 if version_16 == 0 {
266 version_16 = 1;
267 }
268 } else if descriptor.mouse.is_some() {
269 vendor_16 = FUCHSIA_VENDOR_ID;
270 product_16 = FUCHSIA_MOUSE_PRODUCT_ID;
271 if version_16 == 0 {
272 version_16 = 1;
273 }
274 } else if descriptor.consumer_control.is_some() {
275 vendor_16 = FUCHSIA_VENDOR_ID;
276 product_16 = FUCHSIA_KEYBOARD_PRODUCT_ID;
277 if version_16 == 0 {
278 version_16 = 1;
279 }
280 }
281 }
282
283 let default_name = if descriptor.touch.is_some() {
284 "starnix_touch"
285 } else if descriptor.keyboard.is_some() {
286 "starnix_keyboard"
287 } else if descriptor.mouse.is_some() {
288 "starnix_mouse"
289 } else if descriptor.consumer_control.is_some() {
290 "starnix_buttons"
291 } else {
292 "starnix_input"
293 };
294
295 let name = desc_info
296 .and_then(|i| i.product_name.as_ref())
297 .filter(|n| !n.is_empty())
298 .cloned()
299 .unwrap_or_else(|| default_name.to_string());
300
301 let input_id = uapi::input_id {
305 bustype: resolved_bus_type,
306 vendor: vendor_16,
307 product: product_16,
308 version: version_16,
309 };
310
311 InputDeviceInfo::new(input_id, name)
312}
313
314#[derive(Clone)]
315pub struct InputDevice {
316 device_type: InputDeviceId,
317
318 pub open_files: OpenedFiles,
319
320 pub inspect_status: Arc<InputDeviceStatus>,
321
322 pub info: InputDeviceInfoHandle,
323}
324
325impl InputDevice {
326 pub fn new_touch(
327 display_width: i32,
328 display_height: i32,
329 info: InputDeviceInfoHandle,
330 node_name: &str,
331 inspect_node: &fuchsia_inspect::Node,
332 ) -> Self {
333 let node = inspect_node.create_child(node_name);
334 InputDevice {
335 device_type: InputDeviceId::Touch(display_width, display_height),
336 open_files: Default::default(),
337 inspect_status: InputDeviceStatus::new(node),
338 info,
339 }
340 }
341
342 pub fn new_keyboard(
343 info: InputDeviceInfoHandle,
344 node_name: &str,
345 inspect_node: &fuchsia_inspect::Node,
346 ) -> Self {
347 let node = inspect_node.create_child(node_name);
348 InputDevice {
349 device_type: InputDeviceId::Keyboard,
350 open_files: Default::default(),
351 inspect_status: InputDeviceStatus::new(node),
352 info,
353 }
354 }
355
356 pub fn new_mouse(
357 info: InputDeviceInfoHandle,
358 node_name: &str,
359 inspect_node: &fuchsia_inspect::Node,
360 ) -> Self {
361 let node = inspect_node.create_child(node_name);
362 InputDevice {
363 device_type: InputDeviceId::Mouse,
364 open_files: Default::default(),
365 inspect_status: InputDeviceStatus::new(node),
366 info,
367 }
368 }
369
370 pub fn from_device_descriptor(
373 descriptor: &fidl_fuchsia_input_report::DeviceDescriptor,
374 device_info: Option<DeviceInfo>,
375 node_name: &str,
376 inspect_node: &fuchsia_inspect::Node,
377 ) -> Self {
378 let info = parse_device_info(descriptor, device_info);
379 let config = crate::input_file::InputFileConfig::from_device_descriptor(descriptor);
380 let node = inspect_node.create_child(node_name);
381 InputDevice {
382 device_type: InputDeviceId::Descriptor(Arc::new(config)),
383 open_files: Default::default(),
384 inspect_status: InputDeviceStatus::new(node),
385 info,
386 }
387 }
388
389 pub fn config(&self) -> Option<&crate::input_file::InputFileConfig> {
391 match &self.device_type {
392 InputDeviceId::Descriptor(config) => Some(config.as_ref()),
393 _ => None,
394 }
395 }
396
397 pub fn register(self, kernel: &Kernel, minor: u32) -> Result<Device, Errno> {
419 let registry = &kernel.device_registry;
420 let input_class = registry.objects.input_class();
421 let info = self.info.clone();
422
423 let input_device = registry.add_numberless_device(
424 format!("input{minor}").as_str().into(),
425 None,
426 input_class.clone(),
427 move |device, dir| {
428 build_device_directory(device, dir);
429 let name_info = info.clone();
430 dir.entry(
431 "name",
432 BytesFile::new_node(move || Ok(format!("{}\n", name_info.lock().name))),
433 mode!(IFREG, 0o444),
434 );
435 dir.subdir("id", 0o755, |dir| {
436 let fields: [(&str, fn(&input_id) -> u16); 4] = [
437 ("bustype", |id| id.bustype),
438 ("vendor", |id| id.vendor),
439 ("product", |id| id.product),
440 ("version", |id| id.version),
441 ];
442 for (attribute, selector) in fields {
443 let attr_info = info.clone();
444 dir.entry(
445 attribute,
446 BytesFile::new_node(move || {
447 Ok(format!("{:04x}\n", selector(&attr_info.lock().input_id)))
448 }),
449 mode!(IFREG, 0o444),
450 );
451 }
452 });
453 },
454 );
455
456 Ok(registry.add_device_with_dir(
457 kernel,
458 format!("event{minor}").as_str().into(),
459 DeviceMetadata::new(
460 format!("input/event{minor}").into(),
461 StarnixDeviceId::new(INPUT_MAJOR, minor),
462 DeviceMode::Char,
463 ),
464 Some(input_device),
465 input_class,
466 build_device_directory,
467 self,
468 ))
469 }
470
471 pub fn unregister(current_task: &CurrentTask, event_device: Device) {
476 let registry = ¤t_task.kernel().device_registry;
477 let input_device = event_device.parent().cloned();
478 registry.remove_device(current_task, event_device);
479 if let Some(input_device) = input_device {
480 registry.remove_device(current_task, input_device);
481 }
482 }
483
484 pub fn open_internal(&self) -> Box<dyn FileOps> {
485 let input_file = match &self.device_type {
486 InputDeviceId::Touch(display_width, display_height) => {
487 let mut file_nodes = self.inspect_status.file_nodes.lock();
488 let child_node = self
489 .inspect_status
490 .node
491 .create_child(format!("touch_file_{}", file_nodes.len()));
492 let file = Arc::new(InputFile::new_touch(
493 self.info.clone(),
494 *display_width,
495 *display_height,
496 &child_node,
497 ));
498 file_nodes.push(child_node);
499 file
500 }
501 InputDeviceId::Keyboard => {
502 let mut file_nodes = self.inspect_status.file_nodes.lock();
503 let child_node = self
504 .inspect_status
505 .node
506 .create_child(format!("keyboard_file_{}", file_nodes.len()));
507 let file = Arc::new(InputFile::new_keyboard(self.info.clone(), &child_node));
508 file_nodes.push(child_node);
509 file
510 }
511 InputDeviceId::Mouse => {
512 let mut file_nodes = self.inspect_status.file_nodes.lock();
513 let child_node = self
514 .inspect_status
515 .node
516 .create_child(format!("mouse_file_{}", file_nodes.len()));
517 let file = Arc::new(InputFile::new_mouse(self.info.clone(), &child_node));
518 file_nodes.push(child_node);
519 file
520 }
521 InputDeviceId::Descriptor(config) => {
522 let mut file_nodes = self.inspect_status.file_nodes.lock();
523 let child_node = self
524 .inspect_status
525 .node
526 .create_child(format!("descriptor_file_{}", file_nodes.len()));
527 let file = Arc::new(InputFile::new_from_config(
528 self.info.clone(),
529 (**config).clone(),
530 &child_node,
531 ));
532 file_nodes.push(child_node);
533 file
534 }
535 };
536 input_file.init_inspect_status();
537 self.open_files.lock().on_file_opened(&input_file);
538 Box::new(crate::input_file::ArcInputFile(input_file))
539 }
540
541 #[cfg(test)]
542 pub fn open_test(
543 &self,
544 current_task: &CurrentTask,
545 ) -> Result<starnix_core::vfs::FileHandle, Errno> {
546 let input_file = self.open_internal();
547 let root_namespace_node = current_task
548 .lookup_path_from_root(".".into())
549 .expect("failed to get namespace node for root");
550
551 let file_object = starnix_core::vfs::FileObject::new(
552 current_task,
553 input_file,
554 root_namespace_node,
555 OpenFlags::empty(),
556 )
557 .expect("FileObject::new failed");
558 Ok(file_object)
559 }
560}
561
562impl DeviceOps for InputDevice {
563 fn open(
564 &self,
565 _current_task: &CurrentTask,
566 _id: StarnixDeviceId,
567 _node: &NamespaceNode,
568 _flags: OpenFlags,
569 ) -> Result<Box<dyn FileOps>, Errno> {
570 let input_file = self.open_internal();
571 Ok(input_file)
572 }
573}
574
575#[cfg(test)]
576mod test {
577 #![allow(clippy::unused_unit)] use super::*;
580 use crate::input_event_relay::{
581 self, EventProxyMode, KEYBOARD_INPUT_ID, MOUSE_INPUT_ID, StartRelaysArgs, TOUCH_INPUT_ID,
582 };
583 use anyhow::anyhow;
584 use assert_matches::assert_matches;
585 use diagnostics_assertions::{AnyProperty, assert_data_tree};
586 use fidl::endpoints::RequestStream as _;
587 use fidl_fuchsia_ui_input as fuiinput;
588 use fidl_fuchsia_ui_input::MediaButtonsEvent;
589 use fidl_fuchsia_ui_input3 as fuiinput3;
590 use fidl_fuchsia_ui_pointer as fuipointer;
591 use fidl_fuchsia_ui_policy as fuipolicy;
592 use fuchsia_async::DurationExt;
593 use fuipointer::{
594 EventPhase, TouchEvent, TouchInteractionId, TouchPointerSample, TouchSourceV2Marker,
595 };
596 use futures::StreamExt as _;
597 use pretty_assertions::assert_eq;
598 use starnix_core::mm::{MemoryAccessor as _, MemoryAccessorExt as _};
599 use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
600 use starnix_core::task::{EventHandler, Waiter};
601 use starnix_core::testing::{map_memory, spawn_kernel_and_run};
602 use starnix_core::vfs::FileHandle;
603 use starnix_core::vfs::buffers::VecOutputBuffer;
604 use starnix_syscalls::SUCCESS;
605 use starnix_types::time::timeval_from_time;
606 use starnix_uapi::errors::EAGAIN;
607 use starnix_uapi::uapi;
608 use starnix_uapi::user_address::{UserAddress, UserRef};
609 use starnix_uapi::vfs::FdEvents;
610
611 use test_case::test_case;
612 use test_util::assert_near;
613 use zerocopy::FromBytes as _;
614
615 const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
616 async fn start_touch_input(
617 current_task: &CurrentTask,
618 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
619 let inspector = fuchsia_inspect::Inspector::default();
620 start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
621 }
622
623 async fn start_touch_input_inspect(
624 current_task: &CurrentTask,
625 inspector: &fuchsia_inspect::Inspector,
626 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
627 start_touch_input_inspect_and_dimensions(current_task, 700, 1200, &inspector).await
628 }
629
630 async fn init_keyboard_listener(
631 keyboard_stream: &mut fuiinput3::KeyboardRequestStream,
632 ) -> fuiinput3::KeyboardListenerProxy {
633 let keyboard_listener = match keyboard_stream.next().await {
634 Some(Ok(fuiinput3::KeyboardRequest::AddListener {
635 view_ref: _,
636 listener,
637 responder,
638 })) => {
639 let _ = responder.send();
640 listener.into_proxy()
641 }
642 _ => {
643 panic!("Failed to get event");
644 }
645 };
646
647 keyboard_listener
648 }
649
650 async fn init_button_listeners(
651 device_listener_stream: &mut fuipolicy::DeviceListenerRegistryRequestStream,
652 ) -> (fuipolicy::MediaButtonsListenerProxy, fuipolicy::TouchButtonsListenerProxy) {
653 let media_buttons_listener = match device_listener_stream.next().await {
654 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
655 listener,
656 responder,
657 })) => {
658 let _ = responder.send();
659 listener.into_proxy()
660 }
661 _ => {
662 panic!("Failed to get event");
663 }
664 };
665
666 let touch_buttons_listener = match device_listener_stream.next().await {
667 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
668 listener,
669 responder,
670 })) => {
671 let _ = responder.send();
672 listener.into_proxy()
673 }
674 _ => {
675 panic!("Failed to get event");
676 }
677 };
678
679 (media_buttons_listener, touch_buttons_listener)
680 }
681
682 async fn init_device_listener(
683 device_listener_registry_stream: &mut fuiinput::DeviceListenerRegistryRequestStream,
684 ) -> (fuiinput::DeviceListenerProxy, fuiinput::DeviceIteratorRequestStream) {
685 let (device_iterator_client_end, device_iterator_stream) =
686 fidl::endpoints::create_request_stream::<fuiinput::DeviceIteratorMarker>();
687 let device_listener_proxy = match device_listener_registry_stream.next().await {
688 Some(Ok(fuiinput::DeviceListenerRegistryRequest::RegisterListener {
689 listener,
690 responder,
691 })) => {
692 let _ = responder.send(device_iterator_client_end);
693 listener.into_proxy()
694 }
695 _ => {
696 panic!("Failed to initialize device listener");
697 }
698 };
699 (device_listener_proxy, device_iterator_stream)
700 }
701
702 async fn drain_empty_device_iterator(
703 device_iterator_stream: &mut fuiinput::DeviceIteratorRequestStream,
704 ) {
705 match device_iterator_stream.next().await {
706 Some(Ok(fuiinput::DeviceIteratorRequest::GetNext { responder })) => {
707 responder.send(&[]).expect("failed to send empty batch");
708 }
709 _ => panic!("Failed to respond to existing devices iterator GetNext"),
710 }
711 }
712
713 async fn start_touch_input_inspect_and_dimensions(
714 current_task: &CurrentTask,
715 x_max: i32,
716 y_max: i32,
717 inspector: &fuchsia_inspect::Inspector,
718 ) -> (InputDevice, FileHandle, fuipointer::TouchSourceV2RequestStream) {
719 let input_device = InputDevice::new_touch(
720 x_max,
721 y_max,
722 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
723 "touch_device",
724 inspector.root(),
725 );
726 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
727
728 let (touch_source_client_end, touch_source_stream) =
729 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
730
731 let (mouse_source_client_end, _mouse_source_stream) =
732 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
733
734 let (keyboard_proxy, mut keyboard_stream) =
735 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
736 let view_ref_pair =
737 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
738
739 let (device_registry_proxy, mut device_listener_stream) =
740 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
741 );
742
743 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
744 relay.add_touch_device(
745 input_event_relay::DEFAULT_TOUCH_DEVICE_ID,
746 input_device.open_files.clone(),
747 Some(input_device.inspect_status.clone()),
748 );
749 relay.start_relays(
750 ¤t_task.kernel(),
751 StartRelaysArgs {
752 event_proxy_mode: EventProxyMode::None,
753 touch_source_client_end,
754 keyboard_proxy,
755 mouse_source_client_end,
756 view_ref: view_ref_pair.view_ref,
757 registry_proxy: device_registry_proxy,
758 device_listener_registry: None,
759 display_width: x_max,
760 display_height: y_max,
761 },
762 );
763
764 let _ = init_keyboard_listener(&mut keyboard_stream).await;
765 let _ = init_button_listeners(&mut device_listener_stream).await;
766
767 (input_device, input_file, touch_source_stream)
768 }
769
770 async fn start_keyboard_input(
771 current_task: &CurrentTask,
772 ) -> (InputDevice, FileHandle, fuiinput3::KeyboardListenerProxy) {
773 let inspector = fuchsia_inspect::Inspector::default();
774 start_keyboard_input_inspect(current_task, &inspector).await
775 }
776
777 async fn start_keyboard_input_inspect(
778 current_task: &CurrentTask,
779 inspector: &fuchsia_inspect::Inspector,
780 ) -> (InputDevice, FileHandle, fuiinput3::KeyboardListenerProxy) {
781 let input_device = InputDevice::new_keyboard(
782 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
783 "keyboard_device",
784 inspector.root(),
785 );
786 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
787 let (keyboard_proxy, mut keyboard_stream) =
788 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
789 let view_ref_pair =
790 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
791
792 let (device_registry_proxy, mut device_listener_stream) =
793 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
794 );
795
796 let (touch_source_client_end, _touch_source_stream) =
797 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
798
799 let (mouse_source_client_end, _mouse_source_stream) =
800 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
801
802 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
803 relay.add_keyboard_device(
804 input_event_relay::DEFAULT_KEYBOARD_DEVICE_ID,
805 input_device.open_files.clone(),
806 Some(input_device.inspect_status.clone()),
807 );
808 relay.start_relays(
809 current_task.kernel(),
810 StartRelaysArgs {
811 event_proxy_mode: EventProxyMode::None,
812 touch_source_client_end,
813 keyboard_proxy,
814 mouse_source_client_end,
815 view_ref: view_ref_pair.view_ref,
816 registry_proxy: device_registry_proxy,
817 device_listener_registry: None,
818 display_width: 0,
819 display_height: 0,
820 },
821 );
822
823 let keyboard_listener = init_keyboard_listener(&mut keyboard_stream).await;
824 let _ = init_button_listeners(&mut device_listener_stream).await;
825
826 (input_device, input_file, keyboard_listener)
827 }
828
829 async fn start_button_input(
830 current_task: &CurrentTask,
831 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
832 let inspector = fuchsia_inspect::Inspector::default();
833 start_button_input_inspect(current_task, &inspector).await
834 }
835
836 async fn start_button_input_inspect(
837 current_task: &CurrentTask,
838 inspector: &fuchsia_inspect::Inspector,
839 ) -> (InputDevice, FileHandle, fuipolicy::MediaButtonsListenerProxy) {
840 let input_device = InputDevice::new_keyboard(
841 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
842 "keyboard_device",
843 inspector.root(),
844 );
845 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
846 let (device_registry_proxy, mut device_listener_stream) =
847 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
848 );
849
850 let (touch_source_client_end, _touch_source_stream) =
851 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
852 let (mouse_source_client_end, _mouse_source_stream) =
853 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
854 let (keyboard_proxy, mut keyboard_stream) =
855 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
856 let view_ref_pair =
857 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
858
859 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
860 relay.add_keyboard_device(
861 input_event_relay::DEFAULT_KEYBOARD_DEVICE_ID,
862 input_device.open_files.clone(),
863 Some(input_device.inspect_status.clone()),
864 );
865 relay.start_relays(
866 current_task.kernel(),
867 StartRelaysArgs {
868 event_proxy_mode: EventProxyMode::None,
869 touch_source_client_end,
870 keyboard_proxy,
871 mouse_source_client_end,
872 view_ref: view_ref_pair.view_ref,
873 registry_proxy: device_registry_proxy,
874 device_listener_registry: None,
875 display_width: 0,
876 display_height: 0,
877 },
878 );
879
880 let _ = init_keyboard_listener(&mut keyboard_stream).await;
881 let (button_listener, _) = init_button_listeners(&mut device_listener_stream).await;
882
883 (input_device, input_file, button_listener)
884 }
885
886 async fn start_mouse_input(
887 current_task: &CurrentTask,
888 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
889 let inspector = fuchsia_inspect::Inspector::default();
890 start_mouse_input_inspect(current_task, &inspector).await
891 }
892
893 async fn start_mouse_input_inspect(
894 current_task: &CurrentTask,
895 inspector: &fuchsia_inspect::Inspector,
896 ) -> (InputDevice, FileHandle, fuipointer::MouseSourceV2RequestStream) {
897 let input_device = InputDevice::new_mouse(
898 InputDeviceInfo::new(MOUSE_INPUT_ID, "starnix_mouse".to_string()),
899 "mouse_device",
900 inspector.root(),
901 );
902 let input_file = input_device.open_test(current_task).expect("Failed to create input file");
903
904 let (touch_source_client_end, _touch_source_stream) =
905 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
906
907 let (mouse_source_client_end, mouse_source_stream) =
908 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
909
910 let (keyboard_proxy, mut keyboard_stream) =
911 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
912 let view_ref_pair =
913 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
914
915 let (device_registry_proxy, mut device_listener_stream) =
916 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>(
917 );
918
919 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
920 relay.add_mouse_device(
921 input_event_relay::DEFAULT_MOUSE_DEVICE_ID,
922 input_device.open_files.clone(),
923 Some(input_device.inspect_status.clone()),
924 );
925 relay.start_relays(
926 ¤t_task.kernel(),
927 StartRelaysArgs {
928 event_proxy_mode: EventProxyMode::None,
929 touch_source_client_end,
930 keyboard_proxy,
931 mouse_source_client_end,
932 view_ref: view_ref_pair.view_ref,
933 registry_proxy: device_registry_proxy,
934 device_listener_registry: None,
935 display_width: 0,
936 display_height: 0,
937 },
938 );
939
940 let _ = init_keyboard_listener(&mut keyboard_stream).await;
941 let _ = init_button_listeners(&mut device_listener_stream).await;
942
943 (input_device, input_file, mouse_source_stream)
944 }
945
946 fn make_touch_event(pointer_id: u32) -> fuipointer::TouchEvent {
947 make_touch_event_with_phase(EventPhase::Change, pointer_id)
949 }
950
951 fn make_touch_event_with_phase(phase: EventPhase, pointer_id: u32) -> fuipointer::TouchEvent {
952 make_touch_event_with_coords_phase(0.0, 0.0, phase, pointer_id)
953 }
954
955 fn make_touch_event_with_coords_phase(
956 x: f32,
957 y: f32,
958 phase: EventPhase,
959 pointer_id: u32,
960 ) -> fuipointer::TouchEvent {
961 make_touch_event_with_coords_phase_timestamp(x, y, phase, pointer_id, 0)
962 }
963
964 fn make_touch_event_with_coords(x: f32, y: f32, pointer_id: u32) -> fuipointer::TouchEvent {
965 make_touch_event_with_coords_phase(x, y, EventPhase::Change, pointer_id)
966 }
967
968 fn make_touch_event_with_coords_phase_timestamp(
969 x: f32,
970 y: f32,
971 phase: EventPhase,
972 pointer_id: u32,
973 time_nanos: i64,
974 ) -> fuipointer::TouchEvent {
975 make_touch_event_with_coords_phase_timestamp_device_id(
976 x, y, phase, pointer_id, time_nanos, 0,
977 )
978 }
979
980 fn make_empty_touch_event() -> fuipointer::TouchEvent {
981 TouchEvent {
982 pointer_sample: Some(TouchPointerSample {
983 interaction: Some(TouchInteractionId {
984 pointer_id: 0,
985 device_id: 0,
986 interaction_id: 0,
987 }),
988 ..Default::default()
989 }),
990 ..Default::default()
991 }
992 }
993
994 fn make_touch_event_with_coords_phase_timestamp_device_id(
995 x: f32,
996 y: f32,
997 phase: EventPhase,
998 pointer_id: u32,
999 time_nanos: i64,
1000 device_id: u32,
1001 ) -> fuipointer::TouchEvent {
1002 TouchEvent {
1003 timestamp: Some(time_nanos),
1004 pointer_sample: Some(TouchPointerSample {
1005 position_in_viewport: Some([x, y]),
1006 phase: Some(phase),
1008 interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
1009 ..Default::default()
1010 }),
1011 ..Default::default()
1012 }
1013 }
1014
1015 fn make_mouse_wheel_event(ticks: i64) -> fuipointer::MouseEvent {
1016 make_mouse_wheel_event_with_timestamp(ticks, 0)
1017 }
1018
1019 fn make_mouse_wheel_event_with_timestamp(ticks: i64, timestamp: i64) -> fuipointer::MouseEvent {
1020 fuipointer::MouseEvent {
1021 timestamp: Some(timestamp),
1022 pointer_sample: Some(fuipointer::MousePointerSample {
1023 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
1024 scroll_v: Some(ticks),
1025 ..Default::default()
1026 }),
1027 ..Default::default()
1028 }
1029 }
1030
1031 fn read_uapi_events(file: &FileHandle, current_task: &CurrentTask) -> Vec<uapi::input_event> {
1032 std::iter::from_fn(|| {
1033 let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
1034 match file.read(current_task, &mut event_bytes) {
1035 Ok(INPUT_EVENT_SIZE) => Some(
1036 uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
1037 .map_err(|_| anyhow!("failed to read input_event from buffer")),
1038 ),
1039 Ok(other_size) => {
1040 Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
1041 }
1042 Err(Errno { code: EAGAIN, .. }) => None,
1043 Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
1044 }
1045 })
1046 .enumerate()
1047 .map(|(i, read_res)| match read_res {
1048 Ok(event) => event,
1049 Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
1050 })
1051 .collect()
1052 }
1053
1054 async fn answer_next_touch_watch_request(
1056 request_stream: &mut fuipointer::TouchSourceV2RequestStream,
1057 touch_events: Vec<TouchEvent>,
1058 ) {
1059 let control_handle = request_stream.control_handle();
1060 control_handle
1061 .send_on_touch_events(touch_events, 1)
1062 .expect("failure sending OnTouchEvents");
1063 match request_stream.next().await {
1064 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents { .. })) => {}
1065 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1066 }
1067 }
1068
1069 async fn answer_next_mouse_watch_request(
1071 request_stream: &mut fuipointer::MouseSourceV2RequestStream,
1072 mouse_events: Vec<fuipointer::MouseEvent>,
1073 ) {
1074 let control_handle = request_stream.control_handle();
1075 control_handle
1076 .send_on_mouse_events(mouse_events, 1)
1077 .expect("failure sending OnMouseEvents");
1078 match request_stream.next().await {
1079 Some(Ok(fuipointer::MouseSourceV2Request::AcknowledgeEvents { .. })) => {}
1080 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1081 }
1082 }
1083
1084 #[::fuchsia::test]
1085 async fn touch_source_v2_acknowledges_events_with_matching_stamp() {
1086 spawn_kernel_and_run(async move |current_task| {
1087 let (_input_device, _input_file, mut touch_source_stream) =
1088 start_touch_input(¤t_task).await;
1089
1090 let control_handle = touch_source_stream.control_handle();
1091 control_handle
1092 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 10)
1093 .expect("failure sending OnTouchEvents");
1094 match touch_source_stream.next().await {
1095 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
1096 last_acknowledged_event_stamp,
1097 ..
1098 })) => assert_eq!(last_acknowledged_event_stamp, 10),
1099 unexpected => panic!("unexpected request {:?}", unexpected),
1100 }
1101
1102 control_handle
1103 .send_on_touch_events(
1104 vec![
1105 make_empty_touch_event(),
1106 make_empty_touch_event(),
1107 make_empty_touch_event(),
1108 make_empty_touch_event(),
1109 make_empty_touch_event(),
1110 ],
1111 20,
1112 )
1113 .expect("failure sending OnTouchEvents");
1114 match touch_source_stream.next().await {
1115 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
1116 last_acknowledged_event_stamp,
1117 ..
1118 })) => assert_eq!(last_acknowledged_event_stamp, 20),
1119 unexpected => panic!("unexpected request {:?}", unexpected),
1120 }
1121 })
1122 .await;
1123 }
1124
1125 #[::fuchsia::test]
1126 async fn notifies_polling_waiters_of_new_data() {
1127 spawn_kernel_and_run(async move |current_task| {
1128 let (_input_device, input_file, mut touch_source_stream) =
1131 start_touch_input(¤t_task).await;
1132 let waiter1 = Waiter::new();
1133 let waiter2 = Waiter::new();
1134
1135 [&waiter1, &waiter2].iter().for_each(|waiter| {
1137 input_file.wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None);
1138 });
1139 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1140 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1141
1142 answer_next_touch_watch_request(
1144 &mut touch_source_stream,
1145 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1146 )
1147 .await;
1148 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1149 .await;
1150
1151 assert_eq!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1155 assert_eq!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1156 })
1157 .await;
1158 }
1159
1160 #[::fuchsia::test]
1161 async fn notifies_blocked_waiter_of_new_data() {
1162 spawn_kernel_and_run(async move |current_task| {
1163 let kernel = current_task.kernel().clone();
1165 let (_input_device, input_file, mut touch_source_stream) =
1166 start_touch_input(¤t_task).await;
1167 let waiter = Waiter::new();
1168
1169 input_file.wait_async(¤t_task, &waiter, FdEvents::POLLIN, EventHandler::None);
1171
1172 let closure = move |task: &CurrentTask| waiter.wait(&task);
1173
1174 let (waiter_thread, req) = SpawnRequestBuilder::new()
1175 .with_debug_name("input-device-waiter")
1176 .with_sync_closure(closure)
1177 .build_with_async_result();
1178 kernel.kthreads.spawner().spawn_from_request(req);
1179
1180 let mut waiter_thread = Box::pin(waiter_thread);
1181 assert_matches!(futures::poll!(&mut waiter_thread), futures::task::Poll::Pending);
1182
1183 answer_next_touch_watch_request(
1185 &mut touch_source_stream,
1186 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1187 )
1188 .await;
1189
1190 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1196 .await;
1197 })
1198 .await;
1199 }
1200
1201 #[::fuchsia::test]
1202 async fn does_not_notify_polling_waiters_without_new_data() {
1203 spawn_kernel_and_run(async move |current_task| {
1204 let (_input_device, input_file, mut touch_source_stream) =
1207 start_touch_input(¤t_task).await;
1208 let waiter1 = Waiter::new();
1209 let waiter2 = Waiter::new();
1210
1211 [&waiter1, &waiter2].iter().for_each(|waiter| {
1213 input_file.wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None);
1214 });
1215 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1216 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1217
1218 answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
1220
1221 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1225 assert_matches!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1226 })
1227 .await;
1228 }
1229
1230 #[::fuchsia::test]
1243 async fn honors_wait_cancellation() {
1244 spawn_kernel_and_run(async move |current_task| {
1245 let (_input_device, input_file, mut touch_source_stream) =
1248 start_touch_input(¤t_task).await;
1249 let waiter1 = Waiter::new();
1250 let waiter2 = Waiter::new();
1251
1252 let waitkeys = [&waiter1, &waiter2]
1254 .iter()
1255 .map(|waiter| {
1256 input_file
1257 .wait_async(¤t_task, waiter, FdEvents::POLLIN, EventHandler::None)
1258 .expect("wait_async")
1259 })
1260 .collect::<Vec<_>>();
1261
1262 waitkeys.into_iter().next().expect("failed to get first waitkey").cancel();
1264
1265 answer_next_touch_watch_request(
1267 &mut touch_source_stream,
1268 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1269 )
1270 .await;
1271 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1273 .await;
1274
1275 assert_matches!(waiter1.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Err(_));
1279 assert_eq!(waiter2.wait_until(¤t_task, zx::MonotonicInstant::ZERO), Ok(()));
1280 })
1281 .await;
1282 }
1283
1284 #[::fuchsia::test]
1285 async fn query_events() {
1286 spawn_kernel_and_run(async move |current_task| {
1287 let (_input_device, input_file, mut touch_source_stream) =
1290 start_touch_input(¤t_task).await;
1291
1292 assert_eq!(
1294 input_file.query_events(¤t_task).expect("query_events"),
1295 FdEvents::empty(),
1296 "events should be empty before data arrives"
1297 );
1298
1299 answer_next_touch_watch_request(
1301 &mut touch_source_stream,
1302 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1303 )
1304 .await;
1305
1306 answer_next_touch_watch_request(&mut touch_source_stream, vec![make_touch_event(1)])
1308 .await;
1309
1310 assert_eq!(
1312 input_file.query_events(¤t_task).expect("query_events"),
1313 FdEvents::POLLIN | FdEvents::POLLRDNORM,
1314 "events should be POLLIN after data arrives"
1315 );
1316 })
1317 .await;
1318 }
1319
1320 fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1321 make_uapi_input_event_with_timestamp(ty, code, value, 0)
1322 }
1323
1324 fn make_uapi_input_event_with_timestamp(
1325 ty: u32,
1326 code: u32,
1327 value: i32,
1328 time_nanos: i64,
1329 ) -> uapi::input_event {
1330 uapi::input_event {
1331 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time_nanos)),
1332 type_: ty as u16,
1333 code: code as u16,
1334 value,
1335 }
1336 }
1337
1338 #[::fuchsia::test]
1339 async fn touch_event_ignored() {
1340 spawn_kernel_and_run(async move |current_task| {
1341 let inspector = fuchsia_inspect::Inspector::default();
1343
1344 let (_input_device, input_file, mut touch_source_stream) =
1345 start_touch_input_inspect(¤t_task, &inspector).await;
1346
1347 answer_next_touch_watch_request(
1350 &mut touch_source_stream,
1351 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1352 )
1353 .await;
1354
1355 answer_next_touch_watch_request(
1359 &mut touch_source_stream,
1360 vec![make_empty_touch_event()],
1361 )
1362 .await;
1363
1364 let events = read_uapi_events(&input_file, ¤t_task);
1366
1367 assert_eq!(events.len(), 6);
1368
1369 answer_next_touch_watch_request(
1372 &mut touch_source_stream,
1373 vec![make_empty_touch_event()],
1374 )
1375 .await;
1376
1377 let events = read_uapi_events(&input_file, ¤t_task);
1378 assert_eq!(events, vec![]);
1379 assert_data_tree!(inspector, root: {
1380 touch_device: {
1381 active_wake_leases_count: 0u64,
1382 total_events_with_wake_lease_count: 0u64,
1383 total_fidl_events_received_count: 3u64,
1384 total_fidl_events_ignored_count: 2u64,
1385 total_fidl_events_unexpected_count: 0u64,
1386 total_fidl_events_converted_count: 1u64,
1387 total_uapi_events_generated_count: 6u64,
1388 last_generated_uapi_event_timestamp_ns: 0i64,
1389 touch_file_0: {
1390 fidl_events_received_count: 3u64,
1391 fidl_events_ignored_count: 2u64,
1392 fidl_events_unexpected_count: 0u64,
1393 fidl_events_converted_count: 1u64,
1394 uapi_events_generated_count: 6u64,
1395 uapi_events_read_count: 6u64,
1396 fd_read_count: 8u64,
1397 fd_notify_count: 1u64,
1398 last_generated_uapi_event_timestamp_ns: 0i64,
1399 last_read_uapi_event_timestamp_ns: 0i64,
1400 opened_without_nonblock: AnyProperty,
1401 open_timestamp_ns: AnyProperty,
1402 closed: AnyProperty,
1403 close_timestamp_ns: AnyProperty,
1404 },
1405 }
1406 });
1407 })
1408 .await;
1409 }
1410
1411 #[test_case(make_touch_event_with_phase(EventPhase::Add, 1); "touch add for pointer already added")]
1412 #[test_case(make_touch_event_with_phase(EventPhase::Change, 2); "touch change for pointer not added")]
1413 #[test_case(make_touch_event_with_phase(EventPhase::Remove, 2); "touch remove for pointer not added")]
1414 #[test_case(make_touch_event_with_phase(EventPhase::Cancel, 1); "touch cancel")]
1415 #[::fuchsia::test]
1416 async fn touch_event_unexpected(event: TouchEvent) {
1417 spawn_kernel_and_run(async move |current_task| {
1418 let inspector = fuchsia_inspect::Inspector::default();
1420
1421 let (_input_device, input_file, mut touch_source_stream) =
1422 start_touch_input_inspect(¤t_task, &inspector).await;
1423
1424 answer_next_touch_watch_request(
1427 &mut touch_source_stream,
1428 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1429 )
1430 .await;
1431
1432 answer_next_touch_watch_request(
1436 &mut touch_source_stream,
1437 vec![make_empty_touch_event()],
1438 )
1439 .await;
1440
1441 let events = read_uapi_events(&input_file, ¤t_task);
1443
1444 assert_eq!(events.len(), 6);
1445
1446 answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1449
1450 let events = read_uapi_events(&input_file, ¤t_task);
1451 assert_eq!(events, vec![]);
1452 assert_data_tree!(inspector, root: {
1453 touch_device: {
1454 active_wake_leases_count: 0u64,
1455 total_events_with_wake_lease_count: 0u64,
1456 total_fidl_events_received_count: 3u64,
1457 total_fidl_events_ignored_count: 1u64,
1458 total_fidl_events_unexpected_count: 1u64,
1459 total_fidl_events_converted_count: 1u64,
1460 total_uapi_events_generated_count: 6u64,
1461 last_generated_uapi_event_timestamp_ns: 0i64,
1462 touch_file_0: {
1463 fidl_events_received_count: 3u64,
1464 fidl_events_ignored_count: 1u64,
1465 fidl_events_unexpected_count: 1u64,
1466 fidl_events_converted_count: 1u64,
1467 uapi_events_generated_count: 6u64,
1468 uapi_events_read_count: 6u64,
1469 fd_read_count: 8u64,
1470 fd_notify_count: 1u64,
1471 last_generated_uapi_event_timestamp_ns: 0i64,
1472 last_read_uapi_event_timestamp_ns: 0i64,
1473 opened_without_nonblock: AnyProperty,
1474 open_timestamp_ns: AnyProperty,
1475 closed: AnyProperty,
1476 close_timestamp_ns: AnyProperty,
1477 },
1478 }
1479 });
1480 })
1481 .await;
1482 }
1483
1484 #[::fuchsia::test]
1485 async fn translates_touch_add() {
1486 spawn_kernel_and_run(async move |current_task| {
1487 let (_input_device, input_file, mut touch_source_stream) =
1490 start_touch_input(¤t_task).await;
1491
1492 answer_next_touch_watch_request(
1494 &mut touch_source_stream,
1495 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1496 )
1497 .await;
1498
1499 answer_next_touch_watch_request(
1503 &mut touch_source_stream,
1504 vec![make_empty_touch_event()],
1505 )
1506 .await;
1507
1508 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_TRACKING_ID, 1),
1516 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1517 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1518 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1519 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1520 ]
1521 );
1522 })
1523 .await;
1524 }
1525
1526 #[::fuchsia::test]
1527 async fn translates_touch_change() {
1528 spawn_kernel_and_run(async move |current_task| {
1529 let (_input_device, input_file, mut touch_source_stream) =
1532 start_touch_input(¤t_task).await;
1533
1534 answer_next_touch_watch_request(
1536 &mut touch_source_stream,
1537 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1538 )
1539 .await;
1540
1541 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1545 .await;
1546
1547 let events = read_uapi_events(&input_file, ¤t_task);
1549
1550 assert_eq!(events.len(), 6);
1551
1552 answer_next_touch_watch_request(
1554 &mut touch_source_stream,
1555 vec![make_touch_event_with_coords(10.0, 20.0, 1)],
1556 )
1557 .await;
1558
1559 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1561 .await;
1562
1563 let events = read_uapi_events(&input_file, ¤t_task);
1564 assert_eq!(
1565 events,
1566 vec![
1567 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1568 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1569 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1570 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1571 ]
1572 );
1573 })
1574 .await;
1575 }
1576
1577 #[::fuchsia::test]
1578 async fn translates_touch_remove() {
1579 spawn_kernel_and_run(async move |current_task| {
1580 let (_input_device, input_file, mut touch_source_stream) =
1583 start_touch_input(¤t_task).await;
1584
1585 answer_next_touch_watch_request(
1587 &mut touch_source_stream,
1588 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1589 )
1590 .await;
1591
1592 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1596 .await;
1597
1598 let events = read_uapi_events(&input_file, ¤t_task);
1600
1601 assert_eq!(events.len(), 6);
1602
1603 answer_next_touch_watch_request(
1605 &mut touch_source_stream,
1606 vec![make_touch_event_with_phase(EventPhase::Remove, 1)],
1607 )
1608 .await;
1609
1610 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1612 .await;
1613
1614 let events = read_uapi_events(&input_file, ¤t_task);
1615 assert_eq!(
1616 events,
1617 vec![
1618 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1619 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1620 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1621 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1622 ]
1623 );
1624 })
1625 .await;
1626 }
1627
1628 #[::fuchsia::test]
1629 async fn multi_touch_event_sequence() {
1630 spawn_kernel_and_run(async move |current_task| {
1631 let (_input_device, input_file, mut touch_source_stream) =
1634 start_touch_input(¤t_task).await;
1635
1636 answer_next_touch_watch_request(
1638 &mut touch_source_stream,
1639 vec![make_touch_event_with_phase(EventPhase::Add, 1)],
1640 )
1641 .await;
1642
1643 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1645 .await;
1646 let events = read_uapi_events(&input_file, ¤t_task);
1647
1648 assert_eq!(events.len(), 6);
1649
1650 answer_next_touch_watch_request(
1652 &mut touch_source_stream,
1653 vec![
1654 make_touch_event_with_coords(10.0, 20.0, 1),
1655 make_touch_event_with_phase(EventPhase::Add, 2),
1656 ],
1657 )
1658 .await;
1659
1660 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1662 .await;
1663 let events = read_uapi_events(&input_file, ¤t_task);
1664
1665 assert_eq!(
1666 events,
1667 vec![
1668 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1669 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1670 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1671 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1672 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1673 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 0),
1674 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 0),
1675 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1676 ]
1677 );
1678
1679 answer_next_touch_watch_request(
1681 &mut touch_source_stream,
1682 vec![
1683 make_touch_event_with_coords(11.0, 21.0, 1),
1684 make_touch_event_with_coords(101.0, 201.0, 2),
1685 ],
1686 )
1687 .await;
1688
1689 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1691 .await;
1692 let events = read_uapi_events(&input_file, ¤t_task);
1693
1694 assert_eq!(
1695 events,
1696 vec![
1697 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1698 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 11),
1699 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 21),
1700 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1701 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 101),
1702 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 201),
1703 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1704 ]
1705 );
1706
1707 answer_next_touch_watch_request(
1709 &mut touch_source_stream,
1710 vec![
1711 make_touch_event_with_phase(EventPhase::Remove, 1),
1712 make_touch_event_with_coords(102.0, 202.0, 2),
1713 ],
1714 )
1715 .await;
1716
1717 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1719 .await;
1720 let events = read_uapi_events(&input_file, ¤t_task);
1721
1722 assert_eq!(
1723 events,
1724 vec![
1725 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1726 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1727 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1728 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 102),
1729 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 202),
1730 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1731 ]
1732 );
1733
1734 answer_next_touch_watch_request(
1736 &mut touch_source_stream,
1737 vec![make_touch_event_with_phase(EventPhase::Remove, 2)],
1738 )
1739 .await;
1740
1741 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1743 .await;
1744 let events = read_uapi_events(&input_file, ¤t_task);
1745
1746 assert_eq!(
1747 events,
1748 vec![
1749 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 1),
1750 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, -1),
1751 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 0),
1752 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1753 ]
1754 );
1755 })
1756 .await;
1757 }
1758
1759 #[::fuchsia::test]
1760 async fn multi_event_sequence_unsorted_in_one_watch() {
1761 spawn_kernel_and_run(async move |current_task| {
1762 let (_input_device, input_file, mut touch_source_stream) =
1765 start_touch_input(¤t_task).await;
1766
1767 answer_next_touch_watch_request(
1769 &mut touch_source_stream,
1770 vec![
1771 make_touch_event_with_coords_phase_timestamp(
1772 10.0,
1773 20.0,
1774 EventPhase::Change,
1775 1,
1776 100,
1777 ),
1778 make_touch_event_with_coords_phase_timestamp(0.0, 0.0, EventPhase::Add, 1, 1),
1779 ],
1780 )
1781 .await;
1782
1783 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1785 .await;
1786 let events = read_uapi_events(&input_file, ¤t_task);
1787
1788 assert_eq!(
1789 events,
1790 vec![
1791 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 1),
1792 make_uapi_input_event_with_timestamp(
1793 uapi::EV_ABS,
1794 uapi::ABS_MT_TRACKING_ID,
1795 1,
1796 1
1797 ),
1798 make_uapi_input_event_with_timestamp(
1799 uapi::EV_ABS,
1800 uapi::ABS_MT_POSITION_X,
1801 0,
1802 1
1803 ),
1804 make_uapi_input_event_with_timestamp(
1805 uapi::EV_ABS,
1806 uapi::ABS_MT_POSITION_Y,
1807 0,
1808 1
1809 ),
1810 make_uapi_input_event_with_timestamp(uapi::EV_KEY, uapi::BTN_TOUCH, 1, 1),
1811 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 1),
1812 make_uapi_input_event_with_timestamp(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0, 100),
1813 make_uapi_input_event_with_timestamp(
1814 uapi::EV_ABS,
1815 uapi::ABS_MT_POSITION_X,
1816 10,
1817 100
1818 ),
1819 make_uapi_input_event_with_timestamp(
1820 uapi::EV_ABS,
1821 uapi::ABS_MT_POSITION_Y,
1822 20,
1823 100
1824 ),
1825 make_uapi_input_event_with_timestamp(uapi::EV_SYN, uapi::SYN_REPORT, 0, 100),
1826 ]
1827 );
1828 })
1829 .await;
1830 }
1831
1832 #[test_case((0.0, 0.0); "origin")]
1833 #[test_case((100.7, 200.7); "above midpoint")]
1834 #[test_case((100.3, 200.3); "below midpoint")]
1835 #[test_case((100.5, 200.5); "midpoint")]
1836 #[::fuchsia::test]
1837 async fn sends_acceptable_coordinates((x, y): (f32, f32)) {
1838 spawn_kernel_and_run(async move |current_task| {
1839 let (_input_device, input_file, mut touch_source_stream) =
1841 start_touch_input(¤t_task).await;
1842
1843 answer_next_touch_watch_request(
1845 &mut touch_source_stream,
1846 vec![make_touch_event_with_coords_phase(x, y, EventPhase::Add, 1)],
1847 )
1848 .await;
1849
1850 answer_next_touch_watch_request(&mut touch_source_stream, vec![TouchEvent::default()])
1852 .await;
1853 let events = read_uapi_events(&input_file, ¤t_task);
1854
1855 const ACCEPTABLE_ERROR: f32 = 1.0;
1858 let actual_x = events
1859 .iter()
1860 .find(|event| {
1861 event.type_ == uapi::EV_ABS as u16
1862 && event.code == uapi::ABS_MT_POSITION_X as u16
1863 })
1864 .unwrap_or_else(|| panic!("did not find `ABS_X` event in {:?}", events))
1865 .value;
1866 let actual_y = events
1867 .iter()
1868 .find(|event| {
1869 event.type_ == uapi::EV_ABS as u16
1870 && event.code == uapi::ABS_MT_POSITION_Y as u16
1871 })
1872 .unwrap_or_else(|| panic!("did not find `ABS_Y` event in {:?}", events))
1873 .value;
1874 assert_near!(x, actual_x as f32, ACCEPTABLE_ERROR);
1875 assert_near!(y, actual_y as f32, ACCEPTABLE_ERROR);
1876 })
1877 .await;
1878 }
1879
1880 #[::fuchsia::test]
1881 async fn sends_appropriate_ack_to_touch_source_server() {
1882 spawn_kernel_and_run(async move |current_task| {
1883 let (_input_device, _input_file, mut touch_source_stream) =
1884 start_touch_input(¤t_task).await;
1885
1886 let control_handle = touch_source_stream.control_handle();
1887 control_handle
1888 .send_on_touch_events(vec![make_touch_event_with_phase(EventPhase::Add, 2)], 42)
1889 .expect("failure sending OnTouchEvents");
1890 match touch_source_stream.next().await {
1891 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
1892 last_acknowledged_event_stamp,
1893 ..
1894 })) => assert_eq!(last_acknowledged_event_stamp, 42),
1895 unexpected => panic!("unexpected request {:?}", unexpected),
1896 }
1897 })
1898 .await;
1899 }
1900
1901 #[test_case(fidl_fuchsia_input::Key::Escape, uapi::KEY_POWER; "Esc maps to Power")]
1902 #[test_case(fidl_fuchsia_input::Key::A, uapi::KEY_A; "A maps to A")]
1903 #[::fuchsia::test]
1904 async fn sends_keyboard_events(fkey: fidl_fuchsia_input::Key, lkey: u32) {
1905 spawn_kernel_and_run(async move |current_task| {
1906 let (_keyboard_device, keyboard_file, keyboard_listener) =
1907 start_keyboard_input(¤t_task).await;
1908
1909 let key_event = fuiinput3::KeyEvent {
1910 timestamp: Some(0),
1911 type_: Some(fuiinput3::KeyEventType::Pressed),
1912 key: Some(fkey),
1913 ..Default::default()
1914 };
1915
1916 let _ = keyboard_listener.on_key_event(&key_event).await;
1917 std::mem::drop(keyboard_listener); let events = read_uapi_events(&keyboard_file, ¤t_task);
1919 assert_eq!(events.len(), 2);
1920 assert_eq!(events[0].code, lkey as u16);
1921 })
1922 .await;
1923 }
1924
1925 #[::fuchsia::test]
1926 async fn skips_unknown_keyboard_events() {
1927 spawn_kernel_and_run(async move |current_task| {
1928 let inspector = fuchsia_inspect::Inspector::default();
1929 let (_keyboard_device, keyboard_file, keyboard_listener) =
1930 start_keyboard_input_inspect(¤t_task, &inspector).await;
1931
1932 let key_event = fuiinput3::KeyEvent {
1933 timestamp: Some(0),
1934 type_: Some(fuiinput3::KeyEventType::Pressed),
1935 key: Some(fidl_fuchsia_input::Key::AcRefresh),
1936 ..Default::default()
1937 };
1938
1939 let _ = keyboard_listener.on_key_event(&key_event).await;
1940 std::mem::drop(keyboard_listener); let events = read_uapi_events(&keyboard_file, ¤t_task);
1942 assert_eq!(events.len(), 0);
1943
1944 assert_data_tree!(inspector, root: {
1947 keyboard_device: {
1948 active_wake_leases_count: AnyProperty,
1949 total_events_with_wake_lease_count: AnyProperty,
1950 total_fidl_events_received_count: 1u64,
1951 total_fidl_events_ignored_count: 1u64,
1952 total_fidl_events_unexpected_count: 0u64,
1953 total_fidl_events_converted_count: 0u64,
1954 total_uapi_events_generated_count: 0u64,
1955 last_generated_uapi_event_timestamp_ns: 0i64,
1956 keyboard_file_0: {
1957 fidl_events_received_count: 1u64,
1958 fidl_events_ignored_count: 1u64,
1959 fidl_events_unexpected_count: 0u64,
1960 fidl_events_converted_count: 0u64,
1961 uapi_events_generated_count: 0u64,
1962 uapi_events_read_count: 0u64,
1963 fd_read_count: AnyProperty,
1964 fd_notify_count: AnyProperty,
1965 last_generated_uapi_event_timestamp_ns: 0i64,
1966 last_read_uapi_event_timestamp_ns: AnyProperty,
1967 opened_without_nonblock: AnyProperty,
1968 open_timestamp_ns: AnyProperty,
1969 closed: AnyProperty,
1970 close_timestamp_ns: AnyProperty,
1971 }
1972 }
1973 });
1974 })
1975 .await;
1976 }
1977
1978 #[::fuchsia::test]
1979 async fn sends_power_button_events() {
1980 spawn_kernel_and_run(async move |current_task| {
1981 let (_input_device, input_file, buttons_listener) =
1982 start_button_input(¤t_task).await;
1983
1984 let power_event = MediaButtonsEvent {
1985 volume: Some(0),
1986 mic_mute: Some(false),
1987 pause: Some(false),
1988 camera_disable: Some(false),
1989 power: Some(true),
1990 function: Some(false),
1991 ..Default::default()
1992 };
1993
1994 let _ = buttons_listener.on_event(power_event).await;
1995 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
1998 assert_eq!(events.len(), 2);
1999 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2000 assert_eq!(events[0].value, 1);
2001 })
2002 .await;
2003 }
2004
2005 #[::fuchsia::test]
2006 async fn sends_function_button_events() {
2007 spawn_kernel_and_run(async move |current_task| {
2008 let (_input_device, input_file, buttons_listener) =
2009 start_button_input(¤t_task).await;
2010
2011 let function_event = MediaButtonsEvent {
2012 volume: Some(0),
2013 mic_mute: Some(false),
2014 pause: Some(false),
2015 camera_disable: Some(false),
2016 power: Some(false),
2017 function: Some(true),
2018 ..Default::default()
2019 };
2020
2021 let _ = buttons_listener.on_event(function_event).await;
2022 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
2025 assert_eq!(events.len(), 2);
2026 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
2027 assert_eq!(events[0].value, 1);
2028 })
2029 .await;
2030 }
2031
2032 #[::fuchsia::test]
2033 async fn sends_overlapping_button_events() {
2034 spawn_kernel_and_run(async move |current_task| {
2035 let (_input_device, input_file, buttons_listener) =
2036 start_button_input(¤t_task).await;
2037
2038 let power_event = MediaButtonsEvent {
2039 volume: Some(0),
2040 mic_mute: Some(false),
2041 pause: Some(false),
2042 camera_disable: Some(false),
2043 power: Some(true),
2044 function: Some(false),
2045 ..Default::default()
2046 };
2047
2048 let function_event = MediaButtonsEvent {
2049 volume: Some(0),
2050 mic_mute: Some(false),
2051 pause: Some(false),
2052 camera_disable: Some(false),
2053 power: Some(true),
2054 function: Some(true),
2055 ..Default::default()
2056 };
2057
2058 let function_release_event = MediaButtonsEvent {
2059 volume: Some(0),
2060 mic_mute: Some(false),
2061 pause: Some(false),
2062 camera_disable: Some(false),
2063 power: Some(true),
2064 function: Some(false),
2065 ..Default::default()
2066 };
2067
2068 let power_release_event = MediaButtonsEvent {
2069 volume: Some(0),
2070 mic_mute: Some(false),
2071 pause: Some(false),
2072 camera_disable: Some(false),
2073 power: Some(false),
2074 function: Some(false),
2075 ..Default::default()
2076 };
2077
2078 let _ = buttons_listener.on_event(power_event).await;
2079 let _ = buttons_listener.on_event(function_event).await;
2080 let _ = buttons_listener.on_event(function_release_event).await;
2081 let _ = buttons_listener.on_event(power_release_event).await;
2082 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
2085 assert_eq!(events.len(), 8);
2086 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2087 assert_eq!(events[0].value, 1);
2088 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
2089 assert_eq!(events[2].value, 1);
2090 assert_eq!(events[4].code, uapi::KEY_VOLUMEDOWN as u16);
2091 assert_eq!(events[4].value, 0);
2092 assert_eq!(events[6].code, uapi::KEY_POWER as u16);
2093 assert_eq!(events[6].value, 0);
2094 })
2095 .await;
2096 }
2097
2098 #[::fuchsia::test]
2099 async fn sends_simultaneous_button_events() {
2100 spawn_kernel_and_run(async move |current_task| {
2101 let (_input_device, input_file, buttons_listener) =
2102 start_button_input(¤t_task).await;
2103
2104 let power_and_function_event = MediaButtonsEvent {
2105 volume: Some(0),
2106 mic_mute: Some(false),
2107 pause: Some(false),
2108 camera_disable: Some(false),
2109 power: Some(true),
2110 function: Some(true),
2111 ..Default::default()
2112 };
2113
2114 let _ = buttons_listener.on_event(power_and_function_event).await;
2115 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
2118 assert_eq!(events.len(), 4);
2119 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2120 assert_eq!(events[0].value, 1);
2121 assert_eq!(events[2].code, uapi::KEY_VOLUMEDOWN as u16);
2122 assert_eq!(events[2].value, 1);
2123 })
2124 .await;
2125 }
2126
2127 #[::fuchsia::test]
2128 async fn sends_volume_up_button_events() {
2129 spawn_kernel_and_run(async move |current_task| {
2130 let (_input_device, input_file, buttons_listener) =
2131 start_button_input(¤t_task).await;
2132
2133 let volume_up_event = MediaButtonsEvent {
2134 volume: Some(1),
2135 mic_mute: Some(false),
2136 pause: Some(false),
2137 camera_disable: Some(false),
2138 power: Some(false),
2139 function: Some(false),
2140 ..Default::default()
2141 };
2142
2143 let _ = buttons_listener.on_event(volume_up_event).await;
2144 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
2147 assert_eq!(events.len(), 2);
2148 assert_eq!(events[0].code, uapi::KEY_VOLUMEUP as u16);
2149 assert_eq!(events[0].value, 1);
2150 })
2151 .await;
2152 }
2153
2154 #[::fuchsia::test]
2155 async fn sends_volume_down_button_events() {
2156 spawn_kernel_and_run(async move |current_task| {
2157 let (_input_device, input_file, buttons_listener) =
2158 start_button_input(¤t_task).await;
2159
2160 let volume_down_event = MediaButtonsEvent {
2161 volume: Some(-1),
2162 mic_mute: Some(false),
2163 pause: Some(false),
2164 camera_disable: Some(false),
2165 power: Some(false),
2166 function: Some(false),
2167 ..Default::default()
2168 };
2169
2170 let _ = buttons_listener.on_event(volume_down_event).await;
2171 std::mem::drop(buttons_listener); let events = read_uapi_events(&input_file, ¤t_task);
2174 assert_eq!(events.len(), 2);
2175 assert_eq!(events[0].code, uapi::KEY_VOLUMEDOWN as u16);
2176 assert_eq!(events[0].value, 1);
2177 })
2178 .await;
2179 }
2180
2181 #[test_case(1; "Scroll up")]
2182 #[test_case(-1; "Scroll down")]
2183 #[::fuchsia::test]
2184 async fn sends_mouse_wheel_events(ticks: i64) {
2185 spawn_kernel_and_run(async move |current_task| {
2186 let time = 100;
2187 let uapi_event = uapi::input_event {
2188 time: timeval_from_time(zx::MonotonicInstant::from_nanos(time)),
2189 type_: uapi::EV_REL as u16,
2190 code: uapi::REL_WHEEL as u16,
2191 value: ticks as i32,
2192 };
2193
2194 let (_mouse_device, mouse_file, mut mouse_stream) =
2195 start_mouse_input(¤t_task).await;
2196
2197 answer_next_mouse_watch_request(
2198 &mut mouse_stream,
2199 vec![make_mouse_wheel_event_with_timestamp(ticks, time)],
2200 )
2201 .await;
2202
2203 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
2206
2207 let events = read_uapi_events(&mouse_file, ¤t_task);
2208 assert_eq!(events.len(), 2);
2209 assert_eq!(events[0], uapi_event);
2210 })
2211 .await;
2212 }
2213
2214 #[::fuchsia::test]
2215 async fn handles_mouse_move_and_click_events() {
2216 spawn_kernel_and_run(async move |current_task| {
2217 let mouse_move_event = fuipointer::MouseEvent {
2218 timestamp: Some(0),
2219 pointer_sample: Some(fuipointer::MousePointerSample {
2220 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2221 relative_motion: Some([10.0, 20.0]),
2222 ..Default::default()
2223 }),
2224 ..Default::default()
2225 };
2226 let mouse_click_event = fuipointer::MouseEvent {
2227 timestamp: Some(0),
2228 pointer_sample: Some(fuipointer::MousePointerSample {
2229 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2230 pressed_buttons: Some(vec![1]),
2231 ..Default::default()
2232 }),
2233 ..Default::default()
2234 };
2235
2236 let (_mouse_device, mouse_file, mut mouse_stream) =
2237 start_mouse_input(¤t_task).await;
2238
2239 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_move_event]).await;
2241 answer_next_mouse_watch_request(&mut mouse_stream, vec![mouse_click_event]).await;
2242
2243 answer_next_mouse_watch_request(&mut mouse_stream, vec![]).await;
2246
2247 let events = read_uapi_events(&mouse_file, ¤t_task);
2248 assert!(!events.is_empty());
2249 assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
2250 && e.code == uapi::REL_X as u16
2251 && e.value == 10));
2252 assert!(events.iter().any(|e| e.type_ == uapi::EV_REL as u16
2253 && e.code == uapi::REL_Y as u16
2254 && e.value == 20));
2255 assert!(events.iter().any(|e| e.type_ == uapi::EV_KEY as u16
2256 && e.code == uapi::BTN_LEFT as u16
2257 && e.value == 1));
2258 })
2259 .await;
2260 }
2261
2262 #[::fuchsia::test]
2263 async fn touch_input_initialized_with_inspect_node() {
2264 spawn_kernel_and_run(async move |current_task| {
2265 let inspector = fuchsia_inspect::Inspector::default();
2266 let touch_device = InputDevice::new_touch(
2267 1200, 720, InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2270 "touch_device",
2271 &inspector.root(),
2272 );
2273 let _file_obj = touch_device.open_test(¤t_task);
2274
2275 assert_data_tree!(inspector, root: {
2276 touch_device: {
2277 active_wake_leases_count: 0u64,
2278 total_events_with_wake_lease_count: 0u64,
2279 total_fidl_events_received_count: 0u64,
2280 total_fidl_events_ignored_count: 0u64,
2281 total_fidl_events_unexpected_count: 0u64,
2282 total_fidl_events_converted_count: 0u64,
2283 total_uapi_events_generated_count: 0u64,
2284 last_generated_uapi_event_timestamp_ns: 0i64,
2285 touch_file_0: {
2286 fidl_events_received_count: 0u64,
2287 fidl_events_ignored_count: 0u64,
2288 fidl_events_unexpected_count: 0u64,
2289 fidl_events_converted_count: 0u64,
2290 uapi_events_generated_count: 0u64,
2291 uapi_events_read_count: 0u64,
2292 fd_read_count: 0u64,
2293 fd_notify_count: 0u64,
2294 last_generated_uapi_event_timestamp_ns: 0i64,
2295 last_read_uapi_event_timestamp_ns: 0i64,
2296 opened_without_nonblock: AnyProperty,
2297 open_timestamp_ns: AnyProperty,
2298 closed: AnyProperty,
2299 close_timestamp_ns: AnyProperty,
2300 }
2301 }
2302 });
2303 })
2304 .await;
2305 }
2306
2307 #[::fuchsia::test]
2308 async fn touch_relay_updates_touch_inspect_status() {
2309 spawn_kernel_and_run(async move |current_task| {
2310 let inspector = fuchsia_inspect::Inspector::default();
2311
2312 let (_input_device, input_file, mut touch_source_stream) =
2313 start_touch_input_inspect(¤t_task, &inspector).await;
2314
2315 let control_handle = touch_source_stream.control_handle();
2318 control_handle
2319 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2320 .expect("failure sending OnTouchEvents");
2321 match touch_source_stream.next().await {
2322 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2323 last_acknowledged_event_stamp,
2324 ..
2325 })) => assert_eq!(last_acknowledged_event_stamp, 2),
2326 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2327 }
2328
2329 control_handle
2332 .send_on_touch_events(
2333 vec![
2334 make_touch_event_with_coords_phase_timestamp(
2335 0.0,
2336 0.0,
2337 EventPhase::Add,
2338 1,
2339 1000,
2340 ),
2341 make_touch_event_with_coords_phase_timestamp(
2342 1.0,
2343 1.0,
2344 EventPhase::Change,
2345 1,
2346 2000,
2347 ),
2348 make_touch_event_with_coords_phase_timestamp(
2349 2.0,
2350 2.0,
2351 EventPhase::Change,
2352 1,
2353 3000,
2354 ),
2355 make_touch_event_with_coords_phase_timestamp(
2356 3.0,
2357 3.0,
2358 EventPhase::Change,
2359 1,
2360 4000,
2361 ),
2362 make_touch_event_with_coords_phase_timestamp(
2363 3.0,
2364 3.0,
2365 EventPhase::Remove,
2366 1,
2367 5000,
2368 ),
2369 ],
2370 7,
2371 )
2372 .expect("failure sending OnTouchEvents");
2373 match touch_source_stream.next().await {
2374 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2375 last_acknowledged_event_stamp,
2376 ..
2377 })) => assert_eq!(last_acknowledged_event_stamp, 7),
2378 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2379 }
2380
2381 let _events = read_uapi_events(&input_file, ¤t_task);
2382 assert_data_tree!(inspector, root: {
2383 touch_device: {
2384 active_wake_leases_count: 0u64,
2385 total_events_with_wake_lease_count: 0u64,
2386 total_fidl_events_received_count: 7u64,
2387 total_fidl_events_ignored_count: 2u64,
2388 total_fidl_events_unexpected_count: 0u64,
2389 total_fidl_events_converted_count: 5u64,
2390 total_uapi_events_generated_count: 22u64,
2391 last_generated_uapi_event_timestamp_ns: 5000i64,
2392 touch_file_0: {
2393 fidl_events_received_count: 7u64,
2394 fidl_events_ignored_count: 2u64,
2395 fidl_events_unexpected_count: 0u64,
2396 fidl_events_converted_count: 5u64,
2397 uapi_events_generated_count: 22u64,
2398 uapi_events_read_count: 22u64,
2399 fd_read_count: 23u64,
2400 fd_notify_count: 1u64,
2401 last_generated_uapi_event_timestamp_ns: 5000i64,
2402 last_read_uapi_event_timestamp_ns: 5000i64,
2403 opened_without_nonblock: AnyProperty,
2404 open_timestamp_ns: AnyProperty,
2405 closed: AnyProperty,
2406 close_timestamp_ns: AnyProperty,
2407 },
2408 }
2409 });
2410 })
2411 .await;
2412 }
2413
2414 #[::fuchsia::test]
2415 async fn new_file_updates_inspect_status() {
2416 spawn_kernel_and_run(async move |current_task| {
2417 let inspector = fuchsia_inspect::Inspector::default();
2418
2419 let input_device = InputDevice::new_touch(
2420 700,
2421 700,
2422 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2423 "touch_device",
2424 inspector.root(),
2425 );
2426 let input_file_0 =
2427 input_device.open_test(¤t_task).expect("Failed to create input file");
2428
2429 let (touch_source_client_end, mut touch_source_stream) =
2430 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
2431 let (mouse_source_client_end, _mouse_source_stream) =
2432 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
2433 let (keyboard_proxy, mut keyboard_stream) =
2434 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
2435 let view_ref_pair =
2436 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
2437 let (device_registry_proxy, mut device_listener_stream) =
2438 fidl::endpoints::create_sync_proxy_and_stream::<
2439 fuipolicy::DeviceListenerRegistryMarker,
2440 >();
2441
2442 let (mut relay, _relay_handle) = input_event_relay::new_input_relay();
2443 relay.add_touch_device(
2444 input_event_relay::DEFAULT_TOUCH_DEVICE_ID,
2445 input_device.open_files.clone(),
2446 Some(input_device.inspect_status.clone()),
2447 );
2448 relay.start_relays(
2449 ¤t_task.kernel(),
2450 StartRelaysArgs {
2451 event_proxy_mode: EventProxyMode::None,
2452 touch_source_client_end,
2453 keyboard_proxy,
2454 mouse_source_client_end,
2455 view_ref: view_ref_pair.view_ref,
2456 registry_proxy: device_registry_proxy,
2457 device_listener_registry: None,
2458 display_width: 700,
2459 display_height: 700,
2460 },
2461 );
2462
2463 let _ = init_keyboard_listener(&mut keyboard_stream).await;
2464 let _ = init_button_listeners(&mut device_listener_stream).await;
2465
2466 let control_handle = touch_source_stream.control_handle();
2469 control_handle
2470 .send_on_touch_events(vec![make_empty_touch_event(), make_empty_touch_event()], 2)
2471 .expect("failure sending OnTouchEvents");
2472 match touch_source_stream.next().await {
2473 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2474 last_acknowledged_event_stamp,
2475 ..
2476 })) => assert_eq!(last_acknowledged_event_stamp, 2),
2477 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2478 }
2479
2480 input_device.open_files.lock().clear();
2482 drop(input_file_0);
2483
2484 let input_file_1 =
2486 input_device.open_test(¤t_task).expect("Failed to create input file");
2487
2488 control_handle
2491 .send_on_touch_events(
2492 vec![
2493 make_touch_event_with_coords_phase_timestamp(
2494 0.0,
2495 0.0,
2496 EventPhase::Add,
2497 1,
2498 1000,
2499 ),
2500 make_touch_event_with_coords_phase_timestamp(
2501 1.0,
2502 1.0,
2503 EventPhase::Change,
2504 1,
2505 2000,
2506 ),
2507 make_touch_event_with_coords_phase_timestamp(
2508 2.0,
2509 2.0,
2510 EventPhase::Change,
2511 1,
2512 3000,
2513 ),
2514 make_touch_event_with_coords_phase_timestamp(
2515 3.0,
2516 3.0,
2517 EventPhase::Change,
2518 1,
2519 4000,
2520 ),
2521 make_touch_event_with_coords_phase_timestamp(
2522 3.0,
2523 3.0,
2524 EventPhase::Remove,
2525 1,
2526 5000,
2527 ),
2528 ],
2529 7,
2530 )
2531 .expect("failure sending OnTouchEvents");
2532 match touch_source_stream.next().await {
2533 Some(Ok(fuipointer::TouchSourceV2Request::AcknowledgeEvents {
2534 last_acknowledged_event_stamp,
2535 ..
2536 })) => assert_eq!(last_acknowledged_event_stamp, 7),
2537 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
2538 }
2539
2540 let _events = read_uapi_events(&input_file_1, ¤t_task);
2541
2542 input_device.open_files.lock().clear();
2544 drop(input_file_1);
2545
2546 assert_data_tree!(inspector, root: {
2547 touch_device: {
2548 active_wake_leases_count: 0u64,
2549 total_events_with_wake_lease_count: 0u64,
2550 total_fidl_events_received_count: 7u64,
2551 total_fidl_events_ignored_count: 2u64,
2552 total_fidl_events_unexpected_count: 0u64,
2553 total_fidl_events_converted_count: 5u64,
2554 total_uapi_events_generated_count: 22u64,
2555 last_generated_uapi_event_timestamp_ns: 5000i64,
2556 touch_file_0: {
2557 fidl_events_received_count: 2u64,
2558 fidl_events_ignored_count: 2u64,
2559 fidl_events_unexpected_count: 0u64,
2560 fidl_events_converted_count: 0u64,
2561 uapi_events_generated_count: 0u64,
2562 uapi_events_read_count: 0u64,
2563 fd_read_count: 0u64,
2564 fd_notify_count: 0u64,
2565 last_generated_uapi_event_timestamp_ns: 0i64,
2566 last_read_uapi_event_timestamp_ns: 0i64,
2567 opened_without_nonblock: AnyProperty,
2568 open_timestamp_ns: AnyProperty,
2569 closed: AnyProperty,
2570 close_timestamp_ns: AnyProperty,
2571 },
2572 touch_file_1: {
2573 fidl_events_received_count: 5u64,
2574 fidl_events_ignored_count: 0u64,
2575 fidl_events_unexpected_count: 0u64,
2576 fidl_events_converted_count: 5u64,
2577 uapi_events_generated_count: 22u64,
2578 uapi_events_read_count: 22u64,
2579 fd_read_count: 23u64,
2580 fd_notify_count: 1u64,
2581 last_generated_uapi_event_timestamp_ns: 5000i64,
2582 last_read_uapi_event_timestamp_ns: 5000i64,
2583 opened_without_nonblock: AnyProperty,
2584 open_timestamp_ns: AnyProperty,
2585 closed: AnyProperty,
2586 close_timestamp_ns: AnyProperty,
2587 },
2588 }
2589 });
2590 })
2591 .await;
2592 }
2593
2594 #[::fuchsia::test]
2595 async fn file_status_inspect_not_empty_after_close() {
2596 spawn_kernel_and_run(async move |current_task| {
2597 let inspector = fuchsia_inspect::Inspector::default();
2598
2599 let input_device = InputDevice::new_touch(
2600 700,
2601 700,
2602 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2603 "touch_device",
2604 inspector.root(),
2605 );
2606 let file_handle =
2607 input_device.open_test(¤t_task).expect("Failed to create input file");
2608
2609 let status = file_handle
2610 .downcast_file::<crate::input_file::ArcInputFile>()
2611 .unwrap()
2612 .0
2613 .inspect_status
2614 .clone()
2615 .expect("touch file must have status");
2616 status.count_received_events(5);
2617
2618 assert_data_tree!(inspector, root: {
2619 touch_device: {
2620 active_wake_leases_count: AnyProperty,
2621 last_generated_uapi_event_timestamp_ns: AnyProperty,
2622 total_events_with_wake_lease_count: AnyProperty,
2623 total_fidl_events_converted_count: AnyProperty,
2624 total_fidl_events_ignored_count: AnyProperty,
2625 total_fidl_events_received_count: AnyProperty,
2626 total_fidl_events_unexpected_count: AnyProperty,
2627 total_uapi_events_generated_count: AnyProperty,
2628 touch_file_0: {
2629 fidl_events_received_count: 5u64,
2630 fd_notify_count: 0u64,
2631 fd_read_count: 0u64,
2632 fidl_events_converted_count: 0u64,
2633 fidl_events_ignored_count: 0u64,
2634 fidl_events_unexpected_count: 0u64,
2635 last_generated_uapi_event_timestamp_ns: 0i64,
2636 last_read_uapi_event_timestamp_ns: 0i64,
2637 uapi_events_generated_count: 0u64,
2638 uapi_events_read_count: 0u64,
2639 opened_without_nonblock: true,
2640 open_timestamp_ns: AnyProperty,
2641 closed: false,
2642 close_timestamp_ns: 0i64,
2643 }
2644 }
2645 });
2646
2647 drop(status);
2648 input_device.open_files.lock().clear();
2649 drop(file_handle);
2650
2651 assert_data_tree!(inspector, root: {
2652 touch_device: {
2653 active_wake_leases_count: AnyProperty,
2654 last_generated_uapi_event_timestamp_ns: AnyProperty,
2655 total_events_with_wake_lease_count: AnyProperty,
2656 total_fidl_events_converted_count: AnyProperty,
2657 total_fidl_events_ignored_count: AnyProperty,
2658 total_fidl_events_received_count: AnyProperty,
2659 total_fidl_events_unexpected_count: AnyProperty,
2660 total_uapi_events_generated_count: AnyProperty,
2661 touch_file_0: {
2662 fidl_events_received_count: 5u64,
2663 fd_notify_count: 0u64,
2664 fd_read_count: 0u64,
2665 fidl_events_converted_count: 0u64,
2666 fidl_events_ignored_count: 0u64,
2667 fidl_events_unexpected_count: 0u64,
2668 last_generated_uapi_event_timestamp_ns: 0i64,
2669 last_read_uapi_event_timestamp_ns: 0i64,
2670 uapi_events_generated_count: 0u64,
2671 uapi_events_read_count: 0u64,
2672 opened_without_nonblock: true,
2673 open_timestamp_ns: AnyProperty,
2674 closed: AnyProperty,
2675 close_timestamp_ns: AnyProperty,
2676 }
2677 }
2678 });
2679 })
2680 .await;
2681 }
2682
2683 #[::fuchsia::test]
2684 async fn keyboard_input_initialized_with_inspect_node() {
2685 spawn_kernel_and_run(async move |current_task| {
2686 let inspector = fuchsia_inspect::Inspector::default();
2687 let keyboard_device = InputDevice::new_keyboard(
2688 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
2689 "keyboard_device",
2690 &inspector.root(),
2691 );
2692 let _file_obj = keyboard_device.open_test(¤t_task);
2693
2694 assert_data_tree!(inspector, root: {
2695 keyboard_device: {
2696 active_wake_leases_count: 0u64,
2697 total_events_with_wake_lease_count: 0u64,
2698 total_fidl_events_received_count: 0u64,
2699 total_fidl_events_ignored_count: 0u64,
2700 total_fidl_events_unexpected_count: 0u64,
2701 total_fidl_events_converted_count: 0u64,
2702 total_uapi_events_generated_count: 0u64,
2703 last_generated_uapi_event_timestamp_ns: 0i64,
2704 keyboard_file_0: {
2705 fidl_events_received_count: 0u64,
2706 fidl_events_ignored_count: 0u64,
2707 fidl_events_unexpected_count: 0u64,
2708 fidl_events_converted_count: 0u64,
2709 uapi_events_generated_count: 0u64,
2710 uapi_events_read_count: 0u64,
2711 fd_read_count: 0u64,
2712 fd_notify_count: 0u64,
2713 last_generated_uapi_event_timestamp_ns: 0i64,
2714 last_read_uapi_event_timestamp_ns: 0i64,
2715 opened_without_nonblock: AnyProperty,
2716 open_timestamp_ns: AnyProperty,
2717 closed: AnyProperty,
2718 close_timestamp_ns: AnyProperty,
2719 }
2720 }
2721 });
2722 })
2723 .await;
2724 }
2725
2726 #[::fuchsia::test]
2727 async fn button_relay_updates_keyboard_inspect_status() {
2728 spawn_kernel_and_run(async move |current_task| {
2729 let inspector = fuchsia_inspect::Inspector::default();
2730
2731 let (_input_device, input_file, buttons_listener) =
2732 start_button_input_inspect(¤t_task, &inspector).await;
2733
2734 let power_event = MediaButtonsEvent {
2737 volume: Some(0),
2738 mic_mute: Some(false),
2739 pause: Some(false),
2740 camera_disable: Some(false),
2741 power: Some(true),
2742 function: Some(false),
2743 ..Default::default()
2744 };
2745
2746 let power_release_event = MediaButtonsEvent {
2747 volume: Some(0),
2748 mic_mute: Some(false),
2749 pause: Some(false),
2750 camera_disable: Some(false),
2751 power: Some(false),
2752 function: Some(false),
2753 ..Default::default()
2754 };
2755
2756 let _ = buttons_listener.on_event(power_event).await;
2757 let _ = buttons_listener.on_event(power_release_event).await;
2758
2759 let events = read_uapi_events(&input_file, ¤t_task);
2760 assert_eq!(events.len(), 4);
2761 assert_eq!(events[0].code, uapi::KEY_POWER as u16);
2762 assert_eq!(events[0].value, 1);
2763 assert_eq!(events[2].code, uapi::KEY_POWER as u16);
2764 assert_eq!(events[2].value, 0);
2765
2766 let _events = read_uapi_events(&input_file, ¤t_task);
2767
2768 assert_data_tree!(inspector, root: {
2769 keyboard_device: {
2770 active_wake_leases_count: 0u64,
2771 total_events_with_wake_lease_count: 0u64,
2772 total_fidl_events_received_count: 2u64,
2773 total_fidl_events_ignored_count: 0u64,
2774 total_fidl_events_unexpected_count: 0u64,
2775 total_fidl_events_converted_count: 2u64,
2776 total_uapi_events_generated_count: 4u64,
2777 last_generated_uapi_event_timestamp_ns: AnyProperty,
2779 keyboard_file_0: {
2780 fidl_events_received_count: 2u64,
2781 fidl_events_ignored_count: 0u64,
2782 fidl_events_unexpected_count: 0u64,
2783 fidl_events_converted_count: 2u64,
2784 uapi_events_generated_count: 4u64,
2785 uapi_events_read_count: 4u64,
2786 fd_read_count: 6u64,
2787 fd_notify_count: 2u64,
2788 last_generated_uapi_event_timestamp_ns: AnyProperty,
2790 last_read_uapi_event_timestamp_ns: AnyProperty,
2791 opened_without_nonblock: AnyProperty,
2792 open_timestamp_ns: AnyProperty,
2793 closed: AnyProperty,
2794 close_timestamp_ns: AnyProperty,
2795 },
2796 }
2797 });
2798 })
2799 .await;
2800 }
2801
2802 #[::fuchsia::test]
2803 async fn mouse_input_initialized_with_inspect_node() {
2804 spawn_kernel_and_run(async move |current_task| {
2805 let inspector = fuchsia_inspect::Inspector::default();
2806 let mouse_device = InputDevice::new_mouse(
2807 InputDeviceInfo::new(MOUSE_INPUT_ID, "starnix_mouse".to_string()),
2808 "mouse_device",
2809 &inspector.root(),
2810 );
2811 let _file_obj = mouse_device.open_test(¤t_task);
2812
2813 assert_data_tree!(inspector, root: {
2814 mouse_device: {
2815 active_wake_leases_count: 0u64,
2816 total_events_with_wake_lease_count: 0u64,
2817 total_fidl_events_received_count: 0u64,
2818 total_fidl_events_ignored_count: 0u64,
2819 total_fidl_events_unexpected_count: 0u64,
2820 total_fidl_events_converted_count: 0u64,
2821 total_uapi_events_generated_count: 0u64,
2822 last_generated_uapi_event_timestamp_ns: 0i64,
2823 mouse_file_0: {
2824 fidl_events_received_count: 0u64,
2825 fidl_events_ignored_count: 0u64,
2826 fidl_events_unexpected_count: 0u64,
2827 fidl_events_converted_count: 0u64,
2828 uapi_events_generated_count: 0u64,
2829 uapi_events_read_count: 0u64,
2830 fd_read_count: 0u64,
2831 fd_notify_count: 0u64,
2832 last_generated_uapi_event_timestamp_ns: 0i64,
2833 last_read_uapi_event_timestamp_ns: 0i64,
2834 opened_without_nonblock: AnyProperty,
2835 open_timestamp_ns: AnyProperty,
2836 closed: AnyProperty,
2837 close_timestamp_ns: AnyProperty,
2838 }
2839 }
2840 });
2841 })
2842 .await;
2843 }
2844
2845 #[::fuchsia::test]
2846 async fn mouse_relay_updates_mouse_inspect_status() {
2847 spawn_kernel_and_run(async move |current_task| {
2848 let inspector = fuchsia_inspect::Inspector::default();
2849
2850 let (_input_device, input_file, mut mouse_source_stream) =
2851 start_mouse_input_inspect(¤t_task, &inspector).await;
2852
2853 let mouse_move_event = fuipointer::MouseEvent {
2854 timestamp: Some(0),
2855 pointer_sample: Some(fuipointer::MousePointerSample {
2856 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2857 position_in_viewport: Some([50.0, 50.0]),
2858 scroll_v: Some(0),
2859 ..Default::default()
2860 }),
2861 ..Default::default()
2862 };
2863 let mouse_click_event = fuipointer::MouseEvent {
2864 timestamp: Some(0),
2865 pointer_sample: Some(fuipointer::MousePointerSample {
2866 device_id: Some(input_event_relay::DEFAULT_MOUSE_DEVICE_ID),
2867 scroll_v: Some(0),
2868 pressed_buttons: Some(vec![1]),
2869 ..Default::default()
2870 }),
2871 ..Default::default()
2872 };
2873
2874 answer_next_mouse_watch_request(
2877 &mut mouse_source_stream,
2878 vec![mouse_move_event, mouse_click_event],
2879 )
2880 .await;
2881
2882 answer_next_mouse_watch_request(
2885 &mut mouse_source_stream,
2886 (0..5).map(|_| make_mouse_wheel_event(1)).collect(),
2887 )
2888 .await;
2889
2890 answer_next_mouse_watch_request(
2893 &mut mouse_source_stream,
2894 vec![make_mouse_wheel_event_with_timestamp(-1, 5000)],
2895 )
2896 .await;
2897
2898 answer_next_mouse_watch_request(&mut mouse_source_stream, vec![]).await;
2901
2902 let _events = read_uapi_events(&input_file, ¤t_task);
2903 assert_data_tree!(inspector, root: {
2904 mouse_device: {
2905 active_wake_leases_count: 0u64,
2906 total_events_with_wake_lease_count: 0u64,
2907 total_fidl_events_received_count: 8u64,
2908 total_fidl_events_ignored_count: 1u64,
2909 total_fidl_events_unexpected_count: 0u64,
2910 total_fidl_events_converted_count: 7u64,
2911 total_uapi_events_generated_count: 7u64,
2912 last_generated_uapi_event_timestamp_ns: 5000i64,
2913 mouse_file_0: {
2914 fidl_events_received_count: 8u64,
2915 fidl_events_ignored_count: 1u64,
2916 fidl_events_unexpected_count: 0u64,
2917 fidl_events_converted_count: 7u64,
2918 uapi_events_generated_count: 7u64,
2919 uapi_events_read_count: 7u64,
2920 fd_read_count: 8u64,
2921 fd_notify_count: 3u64,
2922 last_generated_uapi_event_timestamp_ns: 5000i64,
2923 last_read_uapi_event_timestamp_ns: 5000i64,
2924 opened_without_nonblock: AnyProperty,
2925 open_timestamp_ns: AnyProperty,
2926 closed: AnyProperty,
2927 close_timestamp_ns: AnyProperty,
2928 },
2929 }
2930 });
2931 })
2932 .await;
2933 }
2934 #[::fuchsia::test]
2935 async fn eviocgname_zero_buffer_length_succeeds() {
2936 spawn_kernel_and_run(async move |current_task| {
2937 let inspector = fuchsia_inspect::Inspector::default();
2938 let touch_device = InputDevice::new_touch(
2939 700,
2940 1200,
2941 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2942 "touch_device",
2943 &inspector.root(),
2944 );
2945 let file =
2946 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2947
2948 let user_addr = starnix_core::testing::map_memory(
2949 ¤t_task,
2950 starnix_uapi::user_address::UserAddress::default(),
2951 4096,
2952 );
2953
2954 let res = file.ioctl(¤t_task, uapi::EVIOCGNAME_0, user_addr.into());
2956 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2957
2958 let name_req = uapi::EVIOCGNAME_0 | (256 << 16);
2960 let res = file.ioctl(¤t_task, name_req, user_addr.into());
2961 assert!(res.is_ok());
2962 let bytes_written = res.unwrap().value() as usize;
2963 assert!(bytes_written > 0);
2964 })
2965 .await;
2966 }
2967
2968 #[::fuchsia::test]
2969 async fn evdev_variable_length_ioctls_succeed() {
2970 spawn_kernel_and_run(async move |current_task| {
2971 let inspector = fuchsia_inspect::Inspector::default();
2972 let touch_device = InputDevice::new_touch(
2973 700,
2974 1200,
2975 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
2976 "touch_device",
2977 &inspector.root(),
2978 );
2979 let file =
2980 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
2981
2982 let user_addr = starnix_core::testing::map_memory(
2983 ¤t_task,
2984 starnix_uapi::user_address::UserAddress::default(),
2985 4096,
2986 );
2987
2988 let res = file.ioctl(¤t_task, uapi::EVIOCGRAB, 1u64.into());
2990 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
2991
2992 let phys_req = crate::input_file::EVIOCGPHYS_BASE | (256 << 16);
2994 let res = file.ioctl(¤t_task, phys_req, user_addr.into());
2995 assert!(res.is_ok());
2996 let bytes_written = res.unwrap().value() as usize;
2997 assert!(bytes_written > 0);
2998
2999 let uniq_req = crate::input_file::EVIOCGUNIQ_BASE | (256 << 16);
3001 let res = file.ioctl(¤t_task, uniq_req, user_addr.into());
3002 assert_eq!(res, starnix_uapi::error!(ENOENT));
3003
3004 let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
3006 let res = file.ioctl(¤t_task, prop_req, user_addr.into());
3007 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
3008
3009 let bit0_req = crate::input_file::EVIOCGBIT_0_BASE | (64 << 16);
3011 let res = file.ioctl(¤t_task, bit0_req, user_addr.into());
3012 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
3013
3014 let snd_req = crate::input_file::EVIOCGBIT_EV_SND_BASE | (64 << 16);
3016 let res = file.ioctl(¤t_task, snd_req, user_addr.into());
3017 assert_eq!(res, Ok(starnix_syscalls::SUCCESS));
3018
3019 let invalid_bit_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
3021 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
3022 | (64 << 16)
3023 | (0x20 + 0x1f);
3024 let res = file.ioctl(¤t_task, invalid_bit_req, user_addr.into());
3025 assert_eq!(res, starnix_uapi::error!(EINVAL));
3026
3027 let unrecognized_evdev_req = (uapi::_IOC_READ << uapi::_IOC_DIRSHIFT)
3029 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
3030 | 0xFF;
3031 let res = file.ioctl(¤t_task, unrecognized_evdev_req, user_addr.into());
3032 assert_eq!(res, starnix_uapi::error!(EINVAL));
3033
3034 let invalid_dir_req = (uapi::_IOC_WRITE << uapi::_IOC_DIRSHIFT)
3036 | ((b'E' as u32) << uapi::_IOC_TYPESHIFT)
3037 | 0x20;
3038 let res = file.ioctl(¤t_task, invalid_dir_req, user_addr.into());
3039 assert_eq!(res, starnix_uapi::error!(EINVAL));
3040 })
3041 .await;
3042 }
3043
3044 #[::fuchsia::test]
3050 async fn evdev_current_state_queries_succeed() {
3051 spawn_kernel_and_run(async move |current_task| {
3052 let inspector = fuchsia_inspect::Inspector::default();
3053 let touch_device = InputDevice::new_touch(
3054 700,
3055 1200,
3056 InputDeviceInfo::new(TOUCH_INPUT_ID, "starnix_touch".to_string()),
3057 "touch_device",
3058 &inspector.root(),
3059 );
3060 let file =
3061 touch_device.open_test(¤t_task).expect("Failed to open touch input file");
3062
3063 let user_addr = starnix_core::testing::map_memory(
3064 ¤t_task,
3065 starnix_uapi::user_address::UserAddress::default(),
3066 4096,
3067 );
3068
3069 const BUF_LEN: usize = 96;
3070 for (name, base) in [
3071 ("EVIOCGKEY", crate::input_file::EVIOCGKEY_BASE),
3072 ("EVIOCGLED", crate::input_file::EVIOCGLED_BASE),
3073 ("EVIOCGSND", crate::input_file::EVIOCGSND_BASE),
3074 ("EVIOCGSW", crate::input_file::EVIOCGSW_BASE),
3075 ] {
3076 current_task
3079 .write_memory(user_addr, &[0xAAu8; BUF_LEN])
3080 .expect("failed to poison buffer");
3081
3082 let req = base | ((BUF_LEN as u32) << 16);
3083 let res = file.ioctl(¤t_task, req, user_addr.into());
3084 assert_eq!(res, Ok(starnix_syscalls::SUCCESS), "{name} should succeed");
3085
3086 let read_back = current_task
3087 .read_memory_to_vec(user_addr, BUF_LEN)
3088 .expect("failed to read back buffer");
3089 assert_eq!(
3090 read_back,
3091 vec![0u8; BUF_LEN],
3092 "{name} should report nothing currently active"
3093 );
3094 }
3095 })
3096 .await;
3097 }
3098 #[::fuchsia::test]
3099 async fn keyboard_input_file_ioctls() {
3100 spawn_kernel_and_run(async move |current_task| {
3101 let inspector = fuchsia_inspect::Inspector::default();
3102 let keyboard_device = InputDevice::new_keyboard(
3103 InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string()),
3104 "keyboard_device",
3105 &inspector.root(),
3106 );
3107 let file = keyboard_device
3108 .open_test(¤t_task)
3109 .expect("Failed to open keyboard input file");
3110
3111 let user_addr = map_memory(¤t_task, UserAddress::default(), 4096);
3112
3113 let name_req = crate::input_file::EVIOCGNAME_BASE | (256 << 16);
3115 let res = file.ioctl(¤t_task, name_req, user_addr.into());
3116 assert!(res.is_ok());
3117
3118 let prop_req = crate::input_file::EVIOCGPROP_BASE | (64 << 16);
3120 let res = file.ioctl(¤t_task, prop_req, user_addr.into());
3121 assert_eq!(res, Ok(SUCCESS));
3122 let prop_buf = current_task.read_memory_to_vec(user_addr, 8).unwrap();
3123 let byte_idx = (uapi::INPUT_PROP_DIRECT / 8) as usize;
3124 let bit_idx = uapi::INPUT_PROP_DIRECT % 8;
3125 assert_ne!(prop_buf[byte_idx] & (1 << bit_idx), 0);
3126
3127 let key_req = crate::input_file::EVIOCGBIT_EV_KEY_BASE | (64 << 16);
3129 let res = file.ioctl(¤t_task, key_req, user_addr.into());
3130 assert_eq!(res, Ok(SUCCESS));
3131 let key_buf = current_task.read_memory_to_vec(user_addr, 64).unwrap();
3132
3133 let check_key = |key_code: u32| {
3134 let byte = (key_code / 8) as usize;
3135 let bit = key_code % 8;
3136 assert_ne!(
3137 key_buf[byte] & (1 << bit),
3138 0,
3139 "Keycode {} should be set in evdev bitmask",
3140 key_code
3141 );
3142 };
3143 check_key(uapi::KEY_A);
3144 check_key(uapi::KEY_Z);
3145 check_key(uapi::KEY_1);
3146 check_key(uapi::KEY_ENTER);
3147 check_key(uapi::KEY_SPACE);
3148 check_key(uapi::KEY_ESC);
3149 check_key(uapi::KEY_POWER);
3150 check_key(uapi::KEY_VOLUMEUP);
3151 check_key(uapi::KEY_VOLUMEDOWN);
3152
3153 let check_key_absent = |key_code: u32, name: &str| {
3158 let byte = (key_code / 8) as usize;
3159 let bit = key_code % 8;
3160 assert_eq!(
3161 key_buf[byte] & (1 << bit),
3162 0,
3163 "{} ({}) should NOT be set in the keyboard evdev bitmask",
3164 name,
3165 key_code
3166 );
3167 };
3168 check_key_absent(uapi::BTN_LEFT, "BTN_LEFT");
3169 check_key_absent(uapi::BTN_TRIGGER, "BTN_TRIGGER");
3170 check_key_absent(uapi::BTN_SOUTH, "BTN_SOUTH");
3171 check_key_absent(uapi::BTN_THUMBR, "BTN_THUMBR");
3172 })
3173 .await;
3174 }
3175
3176 #[::fuchsia::test]
3177 async fn keyboard_events_update_inspect_counters() {
3178 spawn_kernel_and_run(async move |current_task| {
3179 let inspector = fuchsia_inspect::Inspector::default();
3180 let (_keyboard_device, keyboard_file, keyboard_listener) =
3181 start_keyboard_input_inspect(¤t_task, &inspector).await;
3182
3183 let key_event = fuiinput3::KeyEvent {
3184 timestamp: Some(12345),
3185 type_: Some(fuiinput3::KeyEventType::Pressed),
3186 key: Some(fidl_fuchsia_input::Key::A),
3187 ..Default::default()
3188 };
3189
3190 let _ = keyboard_listener.on_key_event(&key_event).await;
3191 std::mem::drop(keyboard_listener);
3192
3193 let events = read_uapi_events(&keyboard_file, ¤t_task);
3195 assert_eq!(events.len(), 2);
3196
3197 assert_data_tree!(inspector, root: {
3201 keyboard_device: {
3202 active_wake_leases_count: AnyProperty,
3203 total_events_with_wake_lease_count: AnyProperty,
3204 total_fidl_events_received_count: 1u64,
3205 total_fidl_events_ignored_count: 0u64,
3206 total_fidl_events_unexpected_count: 0u64,
3207 total_fidl_events_converted_count: 1u64,
3208 total_uapi_events_generated_count: 2u64,
3209 last_generated_uapi_event_timestamp_ns: 12345i64,
3210 keyboard_file_0: {
3211 fidl_events_received_count: 1u64,
3212 fidl_events_ignored_count: 0u64,
3213 fidl_events_unexpected_count: 0u64,
3214 fidl_events_converted_count: 1u64,
3215 uapi_events_generated_count: 2u64,
3216 uapi_events_read_count: 2u64,
3217 fd_read_count: AnyProperty,
3218 fd_notify_count: AnyProperty,
3219 last_generated_uapi_event_timestamp_ns: 12345i64,
3220 last_read_uapi_event_timestamp_ns: AnyProperty,
3221 opened_without_nonblock: AnyProperty,
3222 open_timestamp_ns: AnyProperty,
3223 closed: AnyProperty,
3224 close_timestamp_ns: AnyProperty,
3225 }
3226 }
3227 });
3228 })
3229 .await;
3230 }
3231
3232 async fn wait_for_device(kernel: &Kernel, devt: StarnixDeviceId, present: bool) {
3233 while kernel.device_registry.get_device(devt, DeviceMode::Char).is_ok() != present {
3234 fuchsia_async::Timer::new(
3235 fuchsia_async::MonotonicDuration::from_millis(10).after_now(),
3236 )
3237 .await;
3238 }
3239 }
3240
3241 async fn wait_for_device_listener_registered(
3242 inspector: &fuchsia_inspect::Inspector,
3243 expected: bool,
3244 ) {
3245 loop {
3246 let hierarchy = fuchsia_inspect::reader::read(inspector).await.unwrap();
3247 if let Some(relay_node) = hierarchy.get_child("input_events_relay") {
3248 if let Some(prop) = relay_node.get_property("device_listener_registered") {
3249 if prop.boolean() == Some(expected) {
3250 break;
3251 }
3252 }
3253 }
3254 fuchsia_async::Timer::new(
3255 fuchsia_async::MonotonicDuration::from_millis(10).after_now(),
3256 )
3257 .await;
3258 }
3259 }
3260
3261 async fn wait_for_num_devices(inspector: &fuchsia_inspect::Inspector, expected: u64) {
3262 loop {
3263 let hierarchy = fuchsia_inspect::reader::read(inspector).await.unwrap();
3264 if let Some(relay_node) = hierarchy.get_child("input_events_relay") {
3265 if let Some(prop) = relay_node.get_property("num_devices") {
3266 if prop.uint() == Some(expected) {
3267 break;
3268 }
3269 }
3270 }
3271 fuchsia_async::Timer::new(
3272 fuchsia_async::MonotonicDuration::from_millis(10).after_now(),
3273 )
3274 .await;
3275 }
3276 }
3277
3278 #[::fuchsia::test]
3279 async fn test_dynamic_device_registration() {
3280 spawn_kernel_and_run(async move |current_task| {
3281 let kernel = current_task.kernel();
3282
3283 let (touch_source_client_end, _touch_source_stream) =
3285 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
3286 let (mouse_source_client_end, _mouse_source_stream) =
3287 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
3288 let (keyboard_proxy, mut keyboard_stream) =
3289 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
3290 let view_ref_pair =
3291 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
3292 let (device_registry_proxy, mut device_listener_stream) =
3293 fidl::endpoints::create_sync_proxy_and_stream::<
3294 fuipolicy::DeviceListenerRegistryMarker,
3295 >();
3296
3297 let (device_listener_registry_proxy, mut device_listener_registry_stream) =
3299 fidl::endpoints::create_sync_proxy_and_stream::<
3300 fuiinput::DeviceListenerRegistryMarker,
3301 >();
3302
3303 let inspector = fuchsia_inspect::Inspector::default();
3304
3305 let (relay, relay_handle) = input_event_relay::new_input_relay();
3306 let relay = relay.with_inspect_node(inspector.root());
3307 relay.start_relays(
3308 &kernel,
3309 StartRelaysArgs {
3310 event_proxy_mode: EventProxyMode::None,
3311 touch_source_client_end,
3312 keyboard_proxy,
3313 mouse_source_client_end,
3314 view_ref: view_ref_pair.view_ref,
3315 registry_proxy: device_registry_proxy,
3316 device_listener_registry: Some(device_listener_registry_proxy),
3317 display_width: 700,
3318 display_height: 700,
3319 },
3320 );
3321
3322 let _ = init_keyboard_listener(&mut keyboard_stream).await;
3323 let _ = init_button_listeners(&mut device_listener_stream).await;
3324 let (device_listener_proxy, mut device_iterator_stream) =
3325 init_device_listener(&mut device_listener_registry_stream).await;
3326 drain_empty_device_iterator(&mut device_iterator_stream).await;
3327
3328 let device_id = 42;
3330 let prepopulated_input_id =
3331 uapi::input_id { bustype: 25, vendor: 26, product: 27, version: 28 };
3332 let prepopulated_name = "prepopulated_touch_device".to_string();
3333 relay_handle.update_uinput_device(
3334 device_id,
3335 prepopulated_input_id,
3336 prepopulated_name.clone(),
3337 );
3338
3339 let descriptor = fuiinput::DeviceDescriptor {
3340 touch: Some(fuiinput::TouchDescriptor::default()),
3341 device_information: Some(fuiinput::DeviceInformation {
3342 product_name: Some("test_touch_device".to_string()),
3343 ..Default::default()
3344 }),
3345 ..Default::default()
3346 };
3347 let event = fuiinput::DeviceEvent {
3348 action: Some(fuiinput::Action::Added),
3349 device_id: Some(device_id),
3350 descriptor: Some(descriptor.clone()),
3351 ..Default::default()
3352 };
3353
3354 device_listener_proxy
3355 .on_device_changed(&event)
3356 .expect("Failed to send dynamic device Added event");
3357
3358 let touch_devt = StarnixDeviceId::new(INPUT_MAJOR, 0);
3360 wait_for_device(kernel, touch_devt, true).await;
3361
3362 let info = relay_handle
3364 .device_infos
3365 .lock()
3366 .get(&device_id)
3367 .expect("device_info exists")
3368 .clone();
3369 assert_eq!(info.lock().input_id, prepopulated_input_id);
3370 assert_eq!(info.lock().name, prepopulated_name);
3371
3372 let buttons_device_id = 43;
3374 let descriptor_buttons = fuiinput::DeviceDescriptor {
3375 consumer_control: Some(fuiinput::ConsumerControlDescriptor::default()),
3376 device_information: Some(fuiinput::DeviceInformation {
3377 product_name: Some("test_buttons_device".to_string()),
3378 ..Default::default()
3379 }),
3380 ..Default::default()
3381 };
3382 let event_buttons = fuiinput::DeviceEvent {
3383 action: Some(fuiinput::Action::Added),
3384 device_id: Some(buttons_device_id),
3385 descriptor: Some(descriptor_buttons.clone()),
3386 ..Default::default()
3387 };
3388 device_listener_proxy
3389 .on_device_changed(&event_buttons)
3390 .expect("Failed to send dynamic ConsumerControl device Added event");
3391
3392 let buttons_devt = StarnixDeviceId::new(INPUT_MAJOR, 1);
3393 wait_for_device(kernel, buttons_devt, true).await;
3394
3395 let keyboard_device_id = 44;
3397 let descriptor_keyboard = fuiinput::DeviceDescriptor {
3398 keyboard: Some(fuiinput::KeyboardDescriptor::default()),
3399 device_information: Some(fuiinput::DeviceInformation {
3400 product_name: Some("test_keyboard_device".to_string()),
3401 ..Default::default()
3402 }),
3403 ..Default::default()
3404 };
3405 let event_keyboard = fuiinput::DeviceEvent {
3406 action: Some(fuiinput::Action::Added),
3407 device_id: Some(keyboard_device_id),
3408 descriptor: Some(descriptor_keyboard.clone()),
3409 ..Default::default()
3410 };
3411 device_listener_proxy
3412 .on_device_changed(&event_keyboard)
3413 .expect("Failed to send dynamic Keyboard device Added event");
3414
3415 let keyboard_devt = StarnixDeviceId::new(INPUT_MAJOR, 2);
3416 wait_for_device(kernel, keyboard_devt, true).await;
3417
3418 let mouse_device_id = 45;
3420 let descriptor_mouse = fuiinput::DeviceDescriptor {
3421 mouse: Some(fuiinput::MouseDescriptor::default()),
3422 device_information: Some(fuiinput::DeviceInformation {
3423 product_name: Some("test_mouse_device".to_string()),
3424 ..Default::default()
3425 }),
3426 ..Default::default()
3427 };
3428 let event_mouse = fuiinput::DeviceEvent {
3429 action: Some(fuiinput::Action::Added),
3430 device_id: Some(mouse_device_id),
3431 descriptor: Some(descriptor_mouse.clone()),
3432 ..Default::default()
3433 };
3434 device_listener_proxy
3435 .on_device_changed(&event_mouse)
3436 .expect("Failed to send dynamic Mouse device Added event");
3437
3438 let mouse_devt = StarnixDeviceId::new(INPUT_MAJOR, 3);
3439 wait_for_device(kernel, mouse_devt, true).await;
3440
3441 wait_for_num_devices(&inspector, 4).await;
3442 assert_data_tree!(inspector, root: contains {
3443 input_events_relay: contains {
3444 num_devices: 4u64,
3445 device_listener_registered: true,
3446 }
3447 });
3448
3449 let event_removed = fuiinput::DeviceEvent {
3451 action: Some(fuiinput::Action::Removed),
3452 device_id: Some(device_id),
3453 descriptor: Some(descriptor.clone()),
3454 ..Default::default()
3455 };
3456 device_listener_proxy
3457 .on_device_changed(&event_removed)
3458 .expect("Failed to send dynamic device Removed event");
3459
3460 wait_for_device(kernel, touch_devt, false).await;
3462 assert!(relay_handle.device_infos.lock().get(&device_id).is_none());
3463
3464 let replacement_device_id = 46;
3466 let replacement_descriptor = fuiinput::DeviceDescriptor {
3467 touch: Some(fuiinput::TouchDescriptor::default()),
3468 device_information: Some(fuiinput::DeviceInformation {
3469 product_name: Some("test_replacement_touch_device".to_string()),
3470 ..Default::default()
3471 }),
3472 ..Default::default()
3473 };
3474 let event_readd = fuiinput::DeviceEvent {
3475 action: Some(fuiinput::Action::Added),
3476 device_id: Some(replacement_device_id),
3477 descriptor: Some(replacement_descriptor.clone()),
3478 ..Default::default()
3479 };
3480 device_listener_proxy
3481 .on_device_changed(&event_readd)
3482 .expect("Failed to send dynamic device Added event for replacement device");
3483
3484 wait_for_device(kernel, touch_devt, true).await;
3486
3487 let event_replacement_removed = fuiinput::DeviceEvent {
3489 action: Some(fuiinput::Action::Removed),
3490 device_id: Some(replacement_device_id),
3491 descriptor: Some(replacement_descriptor.clone()),
3492 ..Default::default()
3493 };
3494 device_listener_proxy
3495 .on_device_changed(&event_replacement_removed)
3496 .expect("Failed to send dynamic device Removed event for replacement device");
3497
3498 wait_for_device(kernel, touch_devt, false).await;
3499
3500 let event_keyboard_removed = fuiinput::DeviceEvent {
3502 action: Some(fuiinput::Action::Removed),
3503 device_id: Some(keyboard_device_id),
3504 descriptor: Some(descriptor_keyboard.clone()),
3505 ..Default::default()
3506 };
3507 device_listener_proxy
3508 .on_device_changed(&event_keyboard_removed)
3509 .expect("Failed to send dynamic Keyboard device Removed event");
3510
3511 wait_for_device(kernel, keyboard_devt, false).await;
3512
3513 let event_mouse_removed = fuiinput::DeviceEvent {
3515 action: Some(fuiinput::Action::Removed),
3516 device_id: Some(mouse_device_id),
3517 descriptor: Some(descriptor_mouse.clone()),
3518 ..Default::default()
3519 };
3520 device_listener_proxy
3521 .on_device_changed(&event_mouse_removed)
3522 .expect("Failed to send dynamic Mouse device Removed event");
3523
3524 wait_for_device(kernel, mouse_devt, false).await;
3525
3526 wait_for_num_devices(&inspector, 1).await;
3527 assert_data_tree!(inspector, root: contains {
3528 input_events_relay: contains {
3529 num_devices: 1u64,
3530 device_listener_registered: true,
3531 }
3532 });
3533
3534 drop(device_listener_proxy);
3537 wait_for_device_listener_registered(&inspector, false).await;
3538 })
3539 .await;
3540 }
3541
3542 #[::fuchsia::test]
3543 async fn test_existing_devices_iterator() {
3544 spawn_kernel_and_run(async move |current_task| {
3545 let kernel = current_task.kernel();
3546
3547 let (touch_source_client_end, _touch_source_stream) =
3549 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
3550 let (mouse_source_client_end, _mouse_source_stream) =
3551 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
3552 let (keyboard_proxy, mut keyboard_stream) =
3553 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
3554 let view_ref_pair =
3555 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
3556 let (device_registry_proxy, mut device_listener_stream) =
3557 fidl::endpoints::create_sync_proxy_and_stream::<
3558 fuipolicy::DeviceListenerRegistryMarker,
3559 >();
3560
3561 let (device_listener_registry_proxy, mut device_listener_registry_stream) =
3563 fidl::endpoints::create_sync_proxy_and_stream::<
3564 fuiinput::DeviceListenerRegistryMarker,
3565 >();
3566
3567 let inspector = fuchsia_inspect::Inspector::default();
3568
3569 let (relay, _relay_handle) = input_event_relay::new_input_relay();
3571 let relay = relay.with_inspect_node(inspector.root());
3572 relay.start_relays(
3573 &kernel,
3574 StartRelaysArgs {
3575 event_proxy_mode: EventProxyMode::None,
3576 touch_source_client_end,
3577 keyboard_proxy,
3578 mouse_source_client_end,
3579 view_ref: view_ref_pair.view_ref,
3580 registry_proxy: device_registry_proxy,
3581 device_listener_registry: Some(device_listener_registry_proxy),
3582 display_width: 700,
3583 display_height: 700,
3584 },
3585 );
3586
3587 let _ = init_keyboard_listener(&mut keyboard_stream).await;
3589 let _ = init_button_listeners(&mut device_listener_stream).await;
3590 let (_device_listener_proxy, mut device_iterator_stream) =
3591 init_device_listener(&mut device_listener_registry_stream).await;
3592
3593 let touch_event = fuiinput::DeviceEvent {
3596 action: Some(fuiinput::Action::Added),
3597 device_id: Some(10),
3598 descriptor: Some(fuiinput::DeviceDescriptor {
3599 touch: Some(fuiinput::TouchDescriptor::default()),
3600 device_information: Some(fuiinput::DeviceInformation {
3601 product_name: Some("existing_touch_device".to_string()),
3602 ..Default::default()
3603 }),
3604 ..Default::default()
3605 }),
3606 ..Default::default()
3607 };
3608 let keyboard_event = fuiinput::DeviceEvent {
3609 action: Some(fuiinput::Action::Added),
3610 device_id: Some(11),
3611 descriptor: Some(fuiinput::DeviceDescriptor {
3612 keyboard: Some(fuiinput::KeyboardDescriptor::default()),
3613 device_information: Some(fuiinput::DeviceInformation {
3614 product_name: Some("existing_keyboard_device".to_string()),
3615 ..Default::default()
3616 }),
3617 ..Default::default()
3618 }),
3619 ..Default::default()
3620 };
3621
3622 match device_iterator_stream.next().await {
3623 Some(Ok(fuiinput::DeviceIteratorRequest::GetNext { responder })) => {
3624 responder
3625 .send(&[touch_event, keyboard_event])
3626 .expect("failed to send first batch");
3627 }
3628 other => panic!("Unexpected request on DeviceIterator: {:?}", other),
3629 }
3630
3631 match device_iterator_stream.next().await {
3633 Some(Ok(fuiinput::DeviceIteratorRequest::GetNext { responder })) => {
3634 responder.send(&[]).expect("failed to send empty batch");
3635 }
3636 other => panic!("Unexpected request on DeviceIterator: {:?}", other),
3637 }
3638
3639 let touch_devt = StarnixDeviceId::new(INPUT_MAJOR, 0);
3641 wait_for_device(kernel, touch_devt, true).await;
3642
3643 let keyboard_devt = StarnixDeviceId::new(INPUT_MAJOR, 1);
3644 wait_for_device(kernel, keyboard_devt, true).await;
3645
3646 wait_for_num_devices(&inspector, 2).await;
3647 assert_data_tree!(inspector, root: contains {
3648 input_events_relay: contains {
3649 num_devices: 2u64,
3650 device_listener_registered: true,
3651 }
3652 });
3653 })
3654 .await;
3655 }
3656
3657 #[::fuchsia::test]
3658 async fn test_dynamic_device_registration_failure() {
3659 spawn_kernel_and_run(async move |current_task| {
3660 let kernel = current_task.kernel();
3661
3662 let (touch_source_client_end, _touch_source_stream) =
3664 fidl::endpoints::create_request_stream::<TouchSourceV2Marker>();
3665 let (mouse_source_client_end, _mouse_source_stream) =
3666 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceV2Marker>();
3667 let (keyboard_proxy, mut keyboard_stream) =
3668 fidl::endpoints::create_sync_proxy_and_stream::<fuiinput3::KeyboardMarker>();
3669 let view_ref_pair =
3670 fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
3671 let (device_registry_proxy, mut device_listener_stream) =
3672 fidl::endpoints::create_sync_proxy_and_stream::<
3673 fuipolicy::DeviceListenerRegistryMarker,
3674 >();
3675
3676 let (device_listener_registry_proxy, device_listener_registry_stream) =
3679 fidl::endpoints::create_sync_proxy_and_stream::<
3680 fuiinput::DeviceListenerRegistryMarker,
3681 >();
3682 drop(device_listener_registry_stream);
3683
3684 let inspector = fuchsia_inspect::Inspector::default();
3685
3686 let (relay, _relay_handle) = input_event_relay::new_input_relay();
3688 let relay = relay.with_inspect_node(inspector.root());
3689 relay.start_relays(
3690 &kernel,
3691 StartRelaysArgs {
3692 event_proxy_mode: EventProxyMode::None,
3693 touch_source_client_end,
3694 keyboard_proxy,
3695 mouse_source_client_end,
3696 view_ref: view_ref_pair.view_ref,
3697 registry_proxy: device_registry_proxy,
3698 device_listener_registry: Some(device_listener_registry_proxy),
3699 display_width: 700,
3700 display_height: 700,
3701 },
3702 );
3703
3704 let keyboard_listener = init_keyboard_listener(&mut keyboard_stream).await;
3705 let _ = init_button_listeners(&mut device_listener_stream).await;
3706
3707 let key_event = fuiinput3::KeyEvent {
3711 timestamp: Some(0),
3712 type_: Some(fuiinput3::KeyEventType::Pressed),
3713 key: Some(fidl_fuchsia_input::Key::A),
3714 ..Default::default()
3715 };
3716 let _ = keyboard_listener.on_key_event(&key_event).await;
3717
3718 wait_for_device_listener_registered(&inspector, false).await;
3719
3720 assert_data_tree!(inspector, root: contains {
3721 input_events_relay: contains {
3722 num_devices: 0u64,
3723 device_listener_registered: false,
3724 }
3725 });
3726 })
3727 .await;
3728 }
3729
3730 #[::fuchsia::test]
3731 async fn register_nests_event_device_under_input_device() {
3732 spawn_kernel_and_run(async move |current_task| {
3733 let kernel = current_task.kernel();
3734 let inspector = fuchsia_inspect::Inspector::default();
3735 let info = InputDeviceInfo::new(KEYBOARD_INPUT_ID, "starnix_buttons".to_string());
3736 let keyboard_device =
3737 InputDevice::new_keyboard(info.clone(), "keyboard_device", &inspector.root());
3738
3739 let event_device =
3740 keyboard_device.register(kernel, 7).expect("failed to register input device");
3741 assert_eq!(event_device.name(), "event7");
3742 assert_eq!(event_device.parent().map(|parent| parent.name()), Some("input7".into()));
3743
3744 let root = &kernel.device_registry.objects.root;
3745 for path in [
3746 "devices/virtual/input/input7/uevent",
3747 "devices/virtual/input/input7/event7/dev",
3748 "devices/virtual/input/input7/event7/uevent",
3749 "class/input/input7",
3750 "class/input/event7",
3751 "dev/char/13:7",
3752 ] {
3753 assert!(root.lookup(path.into()).is_some(), "missing {path}");
3754 }
3755
3756 let read_attr = |path: &str| -> String {
3757 let node = root.lookup(path.into()).unwrap_or_else(|| panic!("missing {path}"));
3758 let file_ops =
3759 node.create_file_ops(¤t_task, OpenFlags::RDONLY).expect("open attr");
3760 let file = starnix_core::testing::anon_test_file(
3761 ¤t_task,
3762 file_ops,
3763 OpenFlags::RDONLY,
3764 );
3765 let mut buf = VecOutputBuffer::new(64);
3766 let bytes_read = file.read(¤t_task, &mut buf).expect("read attr");
3767 String::from_utf8(Vec::from(&buf.data()[..bytes_read])).expect("utf8 attr")
3768 };
3769 assert_eq!(read_attr("devices/virtual/input/input7/name"), "starnix_buttons\n");
3770 assert_eq!(
3771 read_attr("devices/virtual/input/input7/id/bustype"),
3772 format!("{:04x}\n", KEYBOARD_INPUT_ID.bustype)
3773 );
3774 assert_eq!(
3775 read_attr("devices/virtual/input/input7/id/vendor"),
3776 format!("{:04x}\n", KEYBOARD_INPUT_ID.vendor)
3777 );
3778 assert_eq!(
3779 read_attr("devices/virtual/input/input7/id/product"),
3780 format!("{:04x}\n", KEYBOARD_INPUT_ID.product)
3781 );
3782 assert_eq!(
3783 read_attr("devices/virtual/input/input7/id/version"),
3784 format!("{:04x}\n", KEYBOARD_INPUT_ID.version)
3785 );
3786
3787 *info.lock() = InputDeviceInfo {
3789 input_id: input_id { bustype: 0x0003, vendor: 0x1234, product: 0x5678, version: 9 },
3790 name: "custom_uinput".to_string(),
3791 };
3792 assert_eq!(read_attr("devices/virtual/input/input7/name"), "custom_uinput\n");
3793 assert_eq!(read_attr("devices/virtual/input/input7/id/bustype"), "0003\n");
3794 assert_eq!(read_attr("devices/virtual/input/input7/id/vendor"), "1234\n");
3795 assert_eq!(read_attr("devices/virtual/input/input7/id/product"), "5678\n");
3796 assert_eq!(read_attr("devices/virtual/input/input7/id/version"), "0009\n");
3797
3798 assert!(root.lookup("devices/virtual/input/input7/input".into()).is_none());
3801
3802 InputDevice::unregister(¤t_task, event_device);
3803 for path in [
3804 "devices/virtual/input/input7",
3805 "class/input/input7",
3806 "class/input/event7",
3807 "dev/char/13:7",
3808 ] {
3809 assert!(root.lookup(path.into()).is_none(), "unexpected {path}");
3810 }
3811 })
3812 .await;
3813 }
3814
3815 #[::fuchsia::test]
3816 async fn test_from_device_descriptor_registration() {
3817 spawn_kernel_and_run(async move |current_task| {
3818 let kernel = current_task.kernel();
3819 let inspector = fuchsia_inspect::Inspector::default();
3820
3821 let descriptor = fidl_fuchsia_input_report::DeviceDescriptor {
3822 device_information: Some(fidl_fuchsia_input_report::DeviceInformation {
3823 vendor_id: Some(0x18d1),
3824 product_id: Some(0x2345),
3825 version: Some(1),
3826 product_name: Some("Pixel Touch".to_string()),
3827 ..Default::default()
3828 }),
3829 touch: Some(fidl_fuchsia_input_report::TouchDescriptor {
3830 input: Some(fidl_fuchsia_input_report::TouchInputDescriptor {
3831 contacts: Some(vec![fidl_fuchsia_input_report::ContactInputDescriptor {
3832 position_x: Some(fidl_fuchsia_input_report::Axis {
3833 range: fidl_fuchsia_input_report::Range { min: 0, max: 1080 },
3834 unit: fidl_fuchsia_input_report::Unit {
3835 type_: fidl_fuchsia_input_report::UnitType::Meters,
3836 exponent: -3,
3837 },
3838 }),
3839 position_y: Some(fidl_fuchsia_input_report::Axis {
3840 range: fidl_fuchsia_input_report::Range { min: 0, max: 2400 },
3841 unit: fidl_fuchsia_input_report::Unit {
3842 type_: fidl_fuchsia_input_report::UnitType::Meters,
3843 exponent: -3,
3844 },
3845 }),
3846 pressure: Some(fidl_fuchsia_input_report::Axis {
3847 range: fidl_fuchsia_input_report::Range { min: 0, max: 255 },
3848 unit: fidl_fuchsia_input_report::Unit {
3849 type_: fidl_fuchsia_input_report::UnitType::None,
3850 exponent: 0,
3851 },
3852 }),
3853 contact_width: Some(fidl_fuchsia_input_report::Axis {
3854 range: fidl_fuchsia_input_report::Range { min: 0, max: 100 },
3855 unit: fidl_fuchsia_input_report::Unit {
3856 type_: fidl_fuchsia_input_report::UnitType::None,
3857 exponent: 0,
3858 },
3859 }),
3860 contact_height: Some(fidl_fuchsia_input_report::Axis {
3861 range: fidl_fuchsia_input_report::Range { min: 0, max: 100 },
3862 unit: fidl_fuchsia_input_report::Unit {
3863 type_: fidl_fuchsia_input_report::UnitType::None,
3864 exponent: 0,
3865 },
3866 }),
3867 ..Default::default()
3868 }]),
3869 max_contacts: Some(10),
3870 ..Default::default()
3871 }),
3872 ..Default::default()
3873 }),
3874 ..Default::default()
3875 };
3876
3877 let device_info = DeviceInfo::new(uapi::BUS_I2C as u16, 0x18d1, 0x2345, 1);
3878 let touch_device = InputDevice::from_device_descriptor(
3879 &descriptor,
3880 Some(device_info),
3881 "touch_device",
3882 &inspector.root(),
3883 );
3884
3885 assert!(touch_device.config().is_some());
3886 let config = touch_device.config().unwrap();
3887 assert_eq!(config.x_axis_info.maximum, 1080);
3888 assert_eq!(config.y_axis_info.maximum, 2400);
3889 assert_eq!(config.mt_slot_axis_info.maximum, 9);
3890
3891 let touch_file =
3893 touch_device.open_test(¤t_task).expect("failed to open touch device");
3894 let user_addr = map_memory(¤t_task, UserAddress::default(), 4096);
3895 let res_x = touch_file.ioctl(¤t_task, uapi::EVIOCGABS_X, user_addr.into());
3896 assert_eq!(res_x, Ok(SUCCESS));
3897 let absinfo_x: uapi::input_absinfo =
3898 current_task.read_object(UserRef::new(user_addr)).expect("read absinfo_x");
3899 assert_eq!(absinfo_x.minimum, 0);
3900 assert_eq!(absinfo_x.maximum, 1080);
3901 assert_eq!(absinfo_x.resolution, 1);
3902
3903 let res_y = touch_file.ioctl(¤t_task, uapi::EVIOCGABS_Y, user_addr.into());
3904 assert_eq!(res_y, Ok(SUCCESS));
3905 let absinfo_y: uapi::input_absinfo =
3906 current_task.read_object(UserRef::new(user_addr)).expect("read absinfo_y");
3907 assert_eq!(absinfo_y.minimum, 0);
3908 assert_eq!(absinfo_y.maximum, 2400);
3909 assert_eq!(absinfo_y.resolution, 1);
3910
3911 let res_slot =
3912 touch_file.ioctl(¤t_task, uapi::EVIOCGABS_MT_SLOT, user_addr.into());
3913 assert_eq!(res_slot, Ok(SUCCESS));
3914 let absinfo_slot: uapi::input_absinfo =
3915 current_task.read_object(UserRef::new(user_addr)).expect("read absinfo_slot");
3916 assert_eq!(absinfo_slot.minimum, 0);
3917 assert_eq!(absinfo_slot.maximum, 9);
3918
3919 let res_tracking_id =
3920 touch_file.ioctl(¤t_task, uapi::EVIOCGABS_MT_TRACKING_ID, user_addr.into());
3921 assert_eq!(res_tracking_id, Ok(SUCCESS));
3922 let absinfo_tracking_id: uapi::input_absinfo = current_task
3923 .read_object(UserRef::new(user_addr))
3924 .expect("read absinfo_tracking_id");
3925 assert_eq!(absinfo_tracking_id.minimum, 0);
3926 assert_eq!(absinfo_tracking_id.maximum, i32::MAX);
3927
3928 let res_pressure = touch_file.ioctl(
3929 ¤t_task,
3930 crate::input_file::EVIOCGABS_MT_PRESSURE,
3931 user_addr.into(),
3932 );
3933 assert_eq!(res_pressure, Ok(SUCCESS));
3934 let absinfo_pressure: uapi::input_absinfo =
3935 current_task.read_object(UserRef::new(user_addr)).expect("read absinfo_pressure");
3936 assert_eq!(absinfo_pressure.maximum, 255);
3937
3938 let res_major = touch_file.ioctl(
3939 ¤t_task,
3940 crate::input_file::EVIOCGABS_MT_TOUCH_MAJOR,
3941 user_addr.into(),
3942 );
3943 assert_eq!(res_major, Ok(SUCCESS));
3944 let absinfo_major: uapi::input_absinfo =
3945 current_task.read_object(UserRef::new(user_addr)).expect("read absinfo_major");
3946 assert_eq!(absinfo_major.maximum, 100);
3947
3948 let res_minor = touch_file.ioctl(
3949 ¤t_task,
3950 crate::input_file::EVIOCGABS_MT_TOUCH_MINOR,
3951 user_addr.into(),
3952 );
3953 assert_eq!(res_minor, Ok(SUCCESS));
3954 let absinfo_minor: uapi::input_absinfo =
3955 current_task.read_object(UserRef::new(user_addr)).expect("read absinfo_minor");
3956 assert_eq!(absinfo_minor.maximum, 100);
3957
3958 let event_device =
3959 touch_device.register(kernel, 8).expect("failed to register touch device");
3960 assert_eq!(event_device.name(), "event8");
3961
3962 let root = &kernel.device_registry.objects.root;
3963 let read_attr = |path: &str| -> String {
3964 let node = root.lookup(path.into()).unwrap_or_else(|| panic!("missing {path}"));
3965 let file_ops =
3966 node.create_file_ops(¤t_task, OpenFlags::RDONLY).expect("open attr");
3967 let file = starnix_core::testing::anon_test_file(
3968 ¤t_task,
3969 file_ops,
3970 OpenFlags::RDONLY,
3971 );
3972 let mut buf = VecOutputBuffer::new(64);
3973 let bytes_read = file.read(¤t_task, &mut buf).expect("read attr");
3974 String::from_utf8(Vec::from(&buf.data()[..bytes_read])).expect("utf8 attr")
3975 };
3976
3977 assert_eq!(read_attr("devices/virtual/input/input8/name"), "Pixel Touch\n");
3978 assert_eq!(
3979 read_attr("devices/virtual/input/input8/id/bustype"),
3980 format!("{:04x}\n", uapi::BUS_I2C)
3981 );
3982 assert_eq!(read_attr("devices/virtual/input/input8/id/vendor"), "18d1\n");
3983 assert_eq!(read_attr("devices/virtual/input/input8/id/product"), "2345\n");
3984 assert_eq!(read_attr("devices/virtual/input/input8/id/version"), "0001\n");
3985
3986 InputDevice::unregister(¤t_task, event_device);
3987 assert!(root.lookup("devices/virtual/input/input8".into()).is_none());
3988 })
3989 .await;
3990 }
3991
3992 #[::fuchsia::test]
3993 async fn test_parse_device_info_fallbacks() {
3994 let empty_desc = fidl_fuchsia_input_report::DeviceDescriptor::default();
3996 let handle = parse_device_info(&empty_desc, None);
3997 assert_eq!(handle.lock().input_id.bustype, uapi::BUS_VIRTUAL as u16);
3998 assert_eq!(handle.lock().input_id.vendor, 0);
3999 assert_eq!(handle.lock().input_id.product, 0);
4000 assert_eq!(handle.lock().name, "starnix_input");
4001
4002 let touch_desc = fidl_fuchsia_input_report::DeviceDescriptor {
4004 touch: Some(fidl_fuchsia_input_report::TouchDescriptor::default()),
4005 ..Default::default()
4006 };
4007 let handle = parse_device_info(&touch_desc, None);
4008 assert_eq!(handle.lock().name, "starnix_touch");
4009 assert_eq!(handle.lock().input_id.bustype, uapi::BUS_VIRTUAL as u16);
4010 assert_eq!(handle.lock().input_id.vendor, FUCHSIA_VENDOR_ID);
4011 assert_eq!(handle.lock().input_id.product, FUCHSIA_TOUCH_PRODUCT_ID);
4012
4013 let kbd_desc = fidl_fuchsia_input_report::DeviceDescriptor {
4014 keyboard: Some(fidl_fuchsia_input_report::KeyboardDescriptor::default()),
4015 ..Default::default()
4016 };
4017 let handle = parse_device_info(&kbd_desc, None);
4018 assert_eq!(handle.lock().name, "starnix_keyboard");
4019 assert_eq!(handle.lock().input_id.vendor, FUCHSIA_VENDOR_ID);
4020 assert_eq!(handle.lock().input_id.product, FUCHSIA_KEYBOARD_PRODUCT_ID);
4021
4022 let mouse_desc = fidl_fuchsia_input_report::DeviceDescriptor {
4023 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor::default()),
4024 ..Default::default()
4025 };
4026 let handle = parse_device_info(&mouse_desc, None);
4027 assert_eq!(handle.lock().name, "starnix_mouse");
4028 assert_eq!(handle.lock().input_id.vendor, FUCHSIA_VENDOR_ID);
4029 assert_eq!(handle.lock().input_id.product, FUCHSIA_MOUSE_PRODUCT_ID);
4030
4031 let buttons_desc = fidl_fuchsia_input_report::DeviceDescriptor {
4032 consumer_control: Some(fidl_fuchsia_input_report::ConsumerControlDescriptor::default()),
4033 ..Default::default()
4034 };
4035 let handle = parse_device_info(&buttons_desc, None);
4036 assert_eq!(handle.lock().name, "starnix_buttons");
4037 assert_eq!(handle.lock().input_id.vendor, FUCHSIA_VENDOR_ID);
4038 assert_eq!(handle.lock().input_id.product, FUCHSIA_KEYBOARD_PRODUCT_ID);
4039
4040 let mouse_buttons_desc = fidl_fuchsia_input_report::DeviceDescriptor {
4042 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor::default()),
4043 consumer_control: Some(fidl_fuchsia_input_report::ConsumerControlDescriptor::default()),
4044 ..Default::default()
4045 };
4046 let handle = parse_device_info(&mouse_buttons_desc, None);
4047 assert_eq!(handle.lock().name, "starnix_mouse");
4048 assert_eq!(handle.lock().input_id.vendor, FUCHSIA_VENDOR_ID);
4049 assert_eq!(handle.lock().input_id.product, FUCHSIA_MOUSE_PRODUCT_ID);
4050
4051 let dev_info_vendor = DeviceInfo::new(0, 0x1234, 0, 0);
4053 let handle = parse_device_info(&empty_desc, Some(dev_info_vendor));
4054 assert_eq!(handle.lock().input_id.bustype, uapi::BUS_USB as u16);
4055 assert_eq!(handle.lock().input_id.vendor, 0x1234);
4056
4057 let dev_info_product = DeviceInfo::new(0, 0, 0x5678, 0);
4058 let handle = parse_device_info(&empty_desc, Some(dev_info_product));
4059 assert_eq!(handle.lock().input_id.bustype, uapi::BUS_USB as u16);
4060 assert_eq!(handle.lock().input_id.product, 0x5678);
4061
4062 let dev_info_i2c = DeviceInfo::new(uapi::BUS_I2C as u16, 0, 0, 0);
4064 let handle = parse_device_info(&empty_desc, Some(dev_info_i2c));
4065 assert_eq!(handle.lock().input_id.bustype, uapi::BUS_I2C as u16);
4066
4067 let dev_info_trunc = DeviceInfo::new(0, 0x10000, 0, 0);
4069 let handle = parse_device_info(&empty_desc, Some(dev_info_trunc));
4070 assert_eq!(handle.lock().input_id.bustype, uapi::BUS_VIRTUAL as u16);
4071 assert_eq!(handle.lock().input_id.vendor, 0);
4072
4073 let empty_name_desc = fidl_fuchsia_input_report::DeviceDescriptor {
4075 device_information: Some(fidl_fuchsia_input_report::DeviceInformation {
4076 product_name: Some("".to_string()),
4077 ..Default::default()
4078 }),
4079 mouse: Some(fidl_fuchsia_input_report::MouseDescriptor::default()),
4080 ..Default::default()
4081 };
4082 let handle = parse_device_info(&empty_name_desc, None);
4083 assert_eq!(handle.lock().name, "starnix_mouse");
4084
4085 let desc_with_info = fidl_fuchsia_input_report::DeviceDescriptor {
4087 device_information: Some(fidl_fuchsia_input_report::DeviceInformation {
4088 vendor_id: Some(0x1234),
4089 product_id: Some(0x5678),
4090 version: Some(0x0001),
4091 ..Default::default()
4092 }),
4093 ..Default::default()
4094 };
4095 let dev_info_upper_bits_only = DeviceInfo::new(0, 0x10000, 0x20000, 0x30000);
4096 let handle = parse_device_info(&desc_with_info, Some(dev_info_upper_bits_only));
4097 assert_eq!(handle.lock().input_id.vendor, 0x1234);
4098 assert_eq!(handle.lock().input_id.product, 0x5678);
4099 assert_eq!(handle.lock().input_id.version, 0x0001);
4100 }
4101}