1use crate::{InputDeviceStatus, InputFile, uinput};
6use fidl::endpoints::{ClientEnd, RequestStream};
7use fidl_fuchsia_ui_input::TouchDeviceInfo;
8use fidl_fuchsia_ui_input3::{
9 KeyEventStatus, KeyboardListenerMarker, KeyboardListenerRequest, KeyboardListenerRequestStream,
10 KeyboardSynchronousProxy,
11};
12use fidl_fuchsia_ui_pointer::{
13 MouseEvent as FidlMouseEvent, TouchEvent as FidlTouchEvent, TouchPointerSample,
14 TouchResponse as FidlTouchResponse, TouchResponseType, {self as fuipointer},
15};
16use fidl_fuchsia_ui_policy as fuipolicy;
17use fidl_fuchsia_ui_views as fuiviews;
18use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
19use futures::channel::oneshot::{self, Sender};
20use futures::executor::block_on;
21use futures::{FutureExt, StreamExt as _};
22use sorted_vec_map::SortedVecMap;
23use starnix_core::power::{
24 ContainerWakingProxy, ContainerWakingStream, create_proxy_for_wake_events_counter,
25};
26use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
27use starnix_core::task::{Kernel, LockedAndTask};
28use starnix_logging::log_warn;
29use starnix_modules_input_event_conversion::button_fuchsia_to_linux::{
30 new_touch_buttons_bitvec, parse_fidl_media_button_event, parse_fidl_touch_button_event,
31};
32use starnix_modules_input_event_conversion::key_fuchsia_to_linux::parse_fidl_keyboard_event_to_linux_input_event;
33use starnix_modules_input_event_conversion::mouse_fuchsia_to_linux::parse_fidl_mouse_events;
34use starnix_modules_input_event_conversion::touch_fuchsia_to_linux::FuchsiaTouchEventToLinuxTouchEventConverter;
35use starnix_sync::Mutex;
36use starnix_uapi::uapi;
37use std::cell::RefCell;
38use std::collections::VecDeque;
39use std::rc::Rc;
40use std::sync::{Arc, Weak};
41
42const INPUT_RELAY_ROLE_NAME: &str = "fuchsia.starnix.kthread.input_relay";
43
44#[derive(Clone, Copy)]
45pub enum EventProxyMode {
46 None,
48
49 WakeContainer,
53}
54
55pub type OpenedFiles = Arc<Mutex<Vec<Weak<InputFile>>>>;
56
57pub enum InputDeviceType {
58 Touch(FuchsiaTouchEventToLinuxTouchEventConverter),
59 Keyboard,
60 Mouse,
61}
62
63impl std::fmt::Display for InputDeviceType {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 InputDeviceType::Touch(_) => write!(f, "touch"),
67 InputDeviceType::Keyboard => write!(f, "keyboard"),
68 InputDeviceType::Mouse => write!(f, "mouse"),
69 }
70 }
71}
72
73pub struct DeviceState {
74 device_type: InputDeviceType,
75 open_files: OpenedFiles,
76 inspect_status: Option<Arc<InputDeviceStatus>>,
77}
78
79pub struct TrackedWakeLease {
80 _lease: fidl::EventPair,
81 device_status: Arc<InputDeviceStatus>,
82}
83
84impl TrackedWakeLease {
85 pub fn new(lease: fidl::EventPair, device_status: Arc<InputDeviceStatus>) -> Self {
86 device_status.increment_active_wake_leases(1);
87 device_status.count_events_with_wake_lease(1);
88 Self { _lease: lease, device_status }
89 }
90}
91
92impl Drop for TrackedWakeLease {
93 fn drop(&mut self) {
94 self.device_status.decrement_active_wake_leases(1);
95 }
96}
97
98pub type DeviceId = u32;
99
100pub const DEFAULT_TOUCH_DEVICE_ID: DeviceId = 0;
101pub const DEFAULT_KEYBOARD_DEVICE_ID: DeviceId = 1;
102pub const DEFAULT_MOUSE_DEVICE_ID: DeviceId = 2;
103
104enum DeviceStateChange {
105 Add(DeviceId, DeviceState, Sender<()>),
106 Remove(DeviceId, Sender<()>),
107}
108
109pub fn new_input_relay() -> (InputEventsRelay, Arc<InputEventsRelayHandle>) {
110 let (sender, receiver) = unbounded();
111
112 (
113 InputEventsRelay { devices: SortedVecMap::new(), receiver },
114 Arc::new(InputEventsRelayHandle { sender }),
115 )
116}
117
118pub struct InputEventsRelayHandle {
119 sender: UnboundedSender<DeviceStateChange>,
120}
121
122impl InputEventsRelayHandle {
123 pub fn add_touch_device(
124 self: &Arc<Self>,
125 device_id: DeviceId,
126 open_files: OpenedFiles,
127 inspect_status: Option<Arc<InputDeviceStatus>>,
128 ) {
129 let (sender, receiver) = oneshot::channel();
130 let _ = self.sender.unbounded_send(DeviceStateChange::Add(
131 device_id,
132 DeviceState {
133 device_type: InputDeviceType::Touch(
134 FuchsiaTouchEventToLinuxTouchEventConverter::create(),
135 ),
136 open_files,
137 inspect_status,
138 },
139 sender,
140 ));
141 let _ = block_on(receiver);
142 }
143
144 pub fn add_keyboard_device(
145 &self,
146 device_id: DeviceId,
147 open_files: OpenedFiles,
148 inspect_status: Option<Arc<InputDeviceStatus>>,
149 ) {
150 let (sender, receiver) = oneshot::channel();
151 let _ = self.sender.unbounded_send(DeviceStateChange::Add(
152 device_id,
153 DeviceState { device_type: InputDeviceType::Keyboard, open_files, inspect_status },
154 sender,
155 ));
156 let _ = block_on(receiver);
157 }
158
159 pub fn remove_device(&self, device_id: DeviceId) {
160 let (sender, receiver) = oneshot::channel();
161 let _ = self.sender.unbounded_send(DeviceStateChange::Remove(device_id, sender));
162 let _ = block_on(receiver);
163 }
164}
165
166pub struct InputEventsRelay {
167 devices: SortedVecMap<DeviceId, DeviceState>,
168 receiver: UnboundedReceiver<DeviceStateChange>,
169}
170
171impl InputEventsRelay {
172 pub fn start_relays(
176 mut self: Self,
177 kernel: &Kernel,
178 event_proxy_mode: EventProxyMode,
179 touch_source_client_end: ClientEnd<fuipointer::TouchSourceMarker>,
180 keyboard: KeyboardSynchronousProxy,
181 mouse_source_client_end: ClientEnd<fuipointer::MouseSourceMarker>,
182 view_ref: fuiviews::ViewRef,
183 registry_proxy: fuipolicy::DeviceListenerRegistrySynchronousProxy,
184 default_touch_device_opened_files: OpenedFiles,
185 default_keyboard_device_opened_files: OpenedFiles,
186 default_mouse_device_opened_files: OpenedFiles,
187 default_touch_device_inspect: Option<Arc<InputDeviceStatus>>,
188 default_keyboard_device_inspect: Option<Arc<InputDeviceStatus>>,
189 default_mouse_device_inspect: Option<Arc<InputDeviceStatus>>,
190 ) {
191 let f = async move |locked_and_task: LockedAndTask<'_>| {
192 let kernel = locked_and_task.current_task().kernel();
193 let previous_touch_event_disposition: Rc<RefCell<Vec<FidlTouchResponse>>> =
195 Default::default();
196 let touch_waking_fn = |p: &fuipointer::TouchSourceProxy| {
197 p.watch(&previous_touch_event_disposition.borrow_mut())
198 };
199 let (mut default_touch_device, touch_waking_proxy) = setup_touch_relay(
200 kernel,
201 event_proxy_mode,
202 touch_source_client_end,
203 default_touch_device_opened_files,
204 default_touch_device_inspect,
205 );
206 let mut touch_future = touch_waking_proxy.call(touch_waking_fn.clone()).fuse();
207
208 let (mut default_mouse_device, mouse_waking_proxy) = setup_mouse_relay(
210 kernel,
211 event_proxy_mode,
212 mouse_source_client_end,
213 default_mouse_device_opened_files,
214 default_mouse_device_inspect,
215 );
216 let mut mouse_future =
217 mouse_waking_proxy.call(fuipointer::MouseSourceProxy::watch).fuse();
218
219 let (mut default_keyboard_device, mut keyboard_event_stream) = setup_keyboard_relay(
221 keyboard,
222 view_ref,
223 default_keyboard_device_opened_files.clone(),
224 default_keyboard_device_inspect.clone(),
225 );
226
227 let (
229 mut default_button_device,
230 mut media_buttons_waking_stream,
231 mut touch_buttons_waking_stream,
232 ) = setup_button_relay(
233 kernel,
234 registry_proxy,
235 event_proxy_mode,
236 default_keyboard_device_opened_files,
237 default_keyboard_device_inspect,
238 );
239 let mut media_buttons_future = media_buttons_waking_stream.next();
240 let mut touch_buttons_future = touch_buttons_waking_stream.next();
241
242 let mut power_was_pressed = false;
243 let mut function_was_pressed = false;
244 let mut touch_buttons_were_pressed = new_touch_buttons_bitvec();
245
246 loop {
247 futures::select! {
248 touch_future_res = touch_future => {
249 match touch_future_res {
250 Ok(touch_events) => {
251 *previous_touch_event_disposition.borrow_mut() =
252 self.process_touch_event(
253 &mut default_touch_device,
254 touch_events,
255 );
256 touch_future = touch_waking_proxy
257 .call(touch_waking_fn.clone())
258 .fuse();
259 }
260 Err(e) => {
261 log_warn!(
262 "error {:?} reading from TouchSourceProxy; input is stopped",
263 e
264 );
265 }
266 }
267 }
268 mouse_future_res = mouse_future => {
269 match mouse_future_res {
270 Ok(mouse_events) => {
271 self.process_mouse_event(&mut default_mouse_device, mouse_events);
272 mouse_future = mouse_waking_proxy
273 .call(fuipointer::MouseSourceProxy::watch)
274 .fuse();
275 }
276 Err(e) => {
277 log_warn!(
278 "error {:?} reading from MouseSourceProxy; input is stopped",
279 e
280 );
281 }
282 }
283 }
284 media_buttons_res = media_buttons_future => {
285 match media_buttons_res {
286 Some(Ok(event)) => {
287 (power_was_pressed, function_was_pressed) =
288 self.process_media_button_event(
289 &mut default_button_device,
290 event,
291 power_was_pressed,
292 function_was_pressed,
293 );
294 }
295 _ => {}
296 }
297 media_buttons_future = media_buttons_waking_stream.next();
298 }
299 touch_buttons_res = touch_buttons_future => {
300 match touch_buttons_res {
301 Some(Ok(event)) => {
302 touch_buttons_were_pressed = self.process_touch_button_event(
303 &mut default_touch_device,
304 event,
305 &touch_buttons_were_pressed,
306 );
307 }
308 _ => {}
309 }
310 touch_buttons_future = touch_buttons_waking_stream.next();
311 }
312 e = keyboard_event_stream.next() => {
313 match e {
314 Some(Ok(request)) => {
315 self.process_keyboard(&mut default_keyboard_device, request);
316 }
317 _ => {}
318 }
319 }
320 e = self.receiver.next() => {
321 match e {
322 Some(event) => {
323 match event {
324 DeviceStateChange::Add(id, device_state, sender) => {
325 self.devices.insert(id, device_state);
326 let _ = sender.send(());
327 }
328 DeviceStateChange::Remove(id, sender) => {
329 self.devices.remove(&id);
330 let _ = sender.send(());
331 }
332 }
333 }
334 _ => {}
335 }
336 }
337 complete => break,
338 }
339 }
340 };
341 let req = SpawnRequestBuilder::new()
342 .with_debug_name("input-event-relay")
343 .with_role(INPUT_RELAY_ROLE_NAME)
344 .with_async_closure(f)
345 .build();
346 kernel.kthreads.spawner().spawn_from_request(req);
347 }
348
349 fn process_touch_event(
350 self: &mut Self,
351 default_touch_device: &mut DeviceState,
352 touch_events: Vec<FidlTouchEvent>,
353 ) -> Vec<FidlTouchResponse> {
354 fuchsia_trace::duration!("input", "starnix_process_touch_event");
355 for e in &touch_events {
356 match e.trace_flow_id {
357 Some(trace_flow_id) => {
358 fuchsia_trace::flow_end!(
359 "input",
360 "dispatch_event_to_client",
361 trace_flow_id.into()
362 );
363 }
364 None => {
365 log_warn!("touch event has not tracing id");
366 }
367 }
368 }
369 let num_received_events: u64 = touch_events.len().try_into().unwrap();
370
371 let previous_event_disposition =
372 touch_events.iter().map(make_response_for_fidl_event).collect();
373
374 let mut num_ignored_events: u64 = 0;
375
376 let (events_by_device, ignored_events) = group_touch_events_by_device_id(touch_events);
378 num_ignored_events += ignored_events;
379
380 for (device_id, mut events) in events_by_device {
381 fuchsia_trace::duration_begin!("input", "starnix_process_per_device_touch_event");
382
383 let dev = self.devices.get_mut(&device_id).unwrap_or(default_touch_device);
384
385 let mut num_converted_events: u64 = 0;
386 let mut num_unexpected_events: u64 = 0;
387 let mut new_events: VecDeque<uapi::input_event> = VecDeque::new();
388
389 #[allow(clippy::collection_is_never_read)]
390 let mut tracked_leases = vec![];
391 for event in &mut events {
392 if let Some(lease) = event.wake_lease.take() {
393 if let Some(status) = &dev.inspect_status {
394 tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
395 }
396 }
397 }
398
399 let last_event_time_ns: i64;
400 if let InputDeviceType::Touch(ref mut converter) = dev.device_type {
401 let mut batch = converter.handle(events);
402 new_events.append(&mut batch.events);
403 num_converted_events += batch.count_converted_fidl_events;
404 num_ignored_events += batch.count_ignored_fidl_events;
405 num_unexpected_events += batch.count_unexpected_fidl_events;
406 last_event_time_ns = batch.last_event_time_ns;
407 } else {
408 fuchsia_trace::duration_end!("input", "starnix_process_per_device_touch_event");
409 log_warn!(
410 "Non touch device received touch events: device_id = {}, device_type = {}",
411 device_id,
412 dev.device_type
413 );
414 continue;
415 }
416
417 if let Some(dev_inspect_status) = &dev.inspect_status {
418 dev_inspect_status.count_total_received_events(num_received_events);
419 dev_inspect_status.count_total_ignored_events(num_ignored_events);
420 dev_inspect_status.count_total_unexpected_events(num_unexpected_events);
421 dev_inspect_status.count_total_converted_events(num_converted_events);
422 dev_inspect_status.count_total_generated_events(
423 new_events.len().try_into().unwrap(),
424 last_event_time_ns,
425 );
426 } else {
427 log_warn!(
428 "unable to record inspect for device_id: {}, device_type: {}",
429 device_id,
430 dev.device_type
431 );
432 }
433
434 fuchsia_trace::duration_end!("input", "starnix_process_per_device_touch_event");
435 dev.open_files.lock().retain(|f| {
436 let Some(file) = f.upgrade() else {
437 log_warn!("Dropping input file for touch that failed to upgrade");
438 return false;
439 };
440 match &file.inspect_status {
441 Some(file_inspect_status) => {
442 file_inspect_status.count_received_events(num_received_events);
443 file_inspect_status.count_ignored_events(num_ignored_events);
444 file_inspect_status.count_unexpected_events(num_unexpected_events);
445 file_inspect_status.count_converted_events(num_converted_events);
446 }
447 None => {
448 log_warn!("unable to record inspect within the input file")
449 }
450 }
451 if !new_events.is_empty() {
452 if let Some(file_inspect_status) = &file.inspect_status {
456 file_inspect_status.count_generated_events(
457 new_events.len().try_into().unwrap(),
458 last_event_time_ns,
459 );
460 }
461 file.add_events(new_events.clone().into_iter().collect());
462 }
463
464 true
465 });
466 }
467
468 previous_event_disposition
469 }
470
471 fn process_keyboard(
472 self: &mut Self,
473 default_keyboard_device: &mut DeviceState,
474 request: KeyboardListenerRequest,
475 ) {
476 match request {
477 KeyboardListenerRequest::OnKeyEvent { event, responder } => {
478 fuchsia_trace::duration!("input", "starnix_process_keyboard_event");
479
480 let new_events = parse_fidl_keyboard_event_to_linux_input_event(
481 &event,
482 uinput::uinput_running(),
483 );
484
485 let dev = match event.device_id {
486 Some(device_id) => {
487 self.devices.get_mut(&device_id).unwrap_or(default_keyboard_device)
488 }
489 None => default_keyboard_device,
490 };
491
492 dev.open_files.lock().retain(|f| {
493 let Some(file) = f.upgrade() else {
494 log_warn!("Dropping input file for keyboard that failed to upgrade");
495 return false;
496 };
497 if !new_events.is_empty() {
498 file.add_events(new_events.clone().into_iter().collect());
499 }
500
501 true
502 });
503
504 responder.send(KeyEventStatus::Handled).expect("");
505 }
506 }
507 }
508
509 fn process_media_button_event(
510 &mut self,
511 default_button_device: &mut DeviceState,
512 button_event: fuipolicy::MediaButtonsListenerRequest,
513 power_was_pressed: bool,
514 function_was_pressed: bool,
515 ) -> (bool, bool) {
516 let mut power_was_pressed_after = false;
517 let mut function_was_pressed_after = false;
518 match button_event {
519 fuipolicy::MediaButtonsListenerRequest::OnEvent { mut event, responder } => {
520 if let Some(trace_flow_id) = event.trace_flow_id {
521 fuchsia_trace::flow_end!(
522 "input",
523 "dispatch_media_buttons_to_listeners",
524 trace_flow_id.into()
525 );
526 }
527 fuchsia_trace::duration!("input", "starnix_process_media_button_event");
528
529 let batch =
530 parse_fidl_media_button_event(&event, power_was_pressed, function_was_pressed);
531
532 power_was_pressed_after = batch.power_is_pressed;
533 function_was_pressed_after = batch.function_is_pressed;
534
535 let (converted_events, ignored_events, generated_events) = match batch.events.len()
536 {
537 0 => (0u64, 1u64, 0u64),
538 len => {
539 if len % 2 == 1 {
540 log_warn!(
541 "unexpectedly received {} events: there should always be an even number of non-empty events.",
542 len
543 );
544 }
545 (1u64, 0u64, len as u64)
546 }
547 };
548
549 let dev = match event.device_id {
550 Some(device_id) => {
551 self.devices.get_mut(&device_id).unwrap_or(default_button_device)
552 }
553 None => default_button_device,
554 };
555
556 #[allow(clippy::collection_is_never_read)]
557 let mut tracked_leases = vec![];
558 if let Some(lease) = event.wake_lease.take() {
559 if let Some(status) = &dev.inspect_status {
560 tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
561 }
562 }
563
564 if let Some(dev_inspect_status) = &dev.inspect_status {
565 dev_inspect_status.count_total_received_events(1);
566 dev_inspect_status.count_total_ignored_events(ignored_events);
567 dev_inspect_status.count_total_converted_events(converted_events);
568 dev_inspect_status.count_total_generated_events(
569 generated_events,
570 batch.event_time.into_nanos().try_into().unwrap(),
571 );
572 } else {
573 log_warn!("unable to record inspect for button device");
574 }
575
576 dev.open_files.lock().retain(|f| {
577 let Some(file) = f.upgrade() else {
578 log_warn!("Dropping input file for buttons that failed to upgrade");
579 return false;
580 };
581 match &file.inspect_status {
582 Some(file_inspect_status) => {
583 file_inspect_status.count_received_events(1);
584 file_inspect_status.count_ignored_events(ignored_events);
585 file_inspect_status.count_converted_events(converted_events);
586 }
587 None => {
588 log_warn!("unable to record inspect within the input file")
589 }
590 }
591 if !batch.events.is_empty() {
592 if let Some(file_inspect_status) = &file.inspect_status {
593 file_inspect_status.count_generated_events(
594 generated_events,
595 batch.event_time.into_nanos().try_into().unwrap(),
596 );
597 }
598 file.add_events(batch.events.clone());
599 }
600
601 true
602 });
603
604 responder.send().expect("media buttons responder failed to respond");
605 }
606 _ => { }
607 }
608
609 (power_was_pressed_after, function_was_pressed_after)
610 }
611
612 fn process_touch_button_event(
613 &mut self,
614 default_touch_device: &mut DeviceState,
615 button_event: fuipolicy::TouchButtonsListenerRequest,
616 touch_buttons_were_pressed: &bit_vec::BitVec,
617 ) -> bit_vec::BitVec {
618 fuchsia_trace::duration!("input", "starnix_process_touch_button_event");
619 match button_event {
620 fuipolicy::TouchButtonsListenerRequest::OnEvent { mut event, responder } => {
621 if let Some(trace_flow_id) = event.trace_flow_id {
622 fuchsia_trace::flow_end!(
623 "input",
624 "dispatch_touch_button_to_listeners",
625 trace_flow_id.into()
626 );
627 }
628 let batch = parse_fidl_touch_button_event(&event, touch_buttons_were_pressed);
629
630 let (converted_events, ignored_events, generated_events) = match batch.events.len()
631 {
632 0 => (0u64, 1u64, 0u64),
633 len => {
634 if len % 2 == 1 {
635 log_warn!(
636 "unexpectedly received {} events: there should always be an even number of non-empty events.",
637 len
638 );
639 }
640 (1u64, 0u64, len as u64)
641 }
642 };
643
644 let device_id = match &event.device_info {
645 Some(TouchDeviceInfo { id: Some(id), .. }) => Some(*id),
646 _ => None,
647 };
648
649 let dev = match device_id {
650 Some(id) => self.devices.get_mut(&id).unwrap_or(default_touch_device),
651 None => default_touch_device,
652 };
653
654 #[allow(clippy::collection_is_never_read)]
655 let mut tracked_leases = vec![];
656 if let Some(lease) = event.wake_lease.take() {
657 if let Some(status) = &dev.inspect_status {
658 tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
659 }
660 }
661
662 if let Some(dev_inspect_status) = &dev.inspect_status {
663 dev_inspect_status.count_total_received_events(1);
664 dev_inspect_status.count_total_ignored_events(ignored_events);
665 dev_inspect_status.count_total_converted_events(converted_events);
666 dev_inspect_status.count_total_generated_events(
667 generated_events,
668 batch.event_time.into_nanos().try_into().unwrap(),
669 );
670 } else {
671 log_warn!("unable to record inspect for touch device");
672 }
673
674 dev.open_files.lock().retain(|f| {
675 let Some(file) = f.upgrade() else {
676 log_warn!("Dropping input file for touch that failed to upgrade");
677 return false;
678 };
679 match &file.inspect_status {
680 Some(file_inspect_status) => {
681 file_inspect_status.count_received_events(1);
682 file_inspect_status.count_ignored_events(ignored_events);
683 file_inspect_status.count_converted_events(converted_events);
684 }
685 None => {
686 log_warn!("unable to record inspect within the input file")
687 }
688 }
689 if !batch.events.is_empty() {
690 if let Some(file_inspect_status) = &file.inspect_status {
691 file_inspect_status.count_generated_events(
692 generated_events,
693 batch.event_time.into_nanos().try_into().unwrap(),
694 );
695 }
696 file.add_events(batch.events.clone());
697 }
698
699 true
700 });
701
702 responder.send().expect("touch buttons responder failed to respond");
703
704 batch.touch_buttons
705 }
706 fuipolicy::TouchButtonsListenerRequest::_UnknownMethod { ordinal, .. } => {
707 log_warn!("Received an unknown method with ordinal {ordinal}");
708 touch_buttons_were_pressed.clone()
709 }
710 }
711 }
712
713 fn process_mouse_event(
714 self: &Self,
715 default_mouse_device: &mut DeviceState,
716 mut mouse_events: Vec<FidlMouseEvent>,
717 ) {
718 let num_received_events: u64 = mouse_events.len().try_into().unwrap();
719 #[allow(clippy::collection_is_never_read)]
720 let mut tracked_leases = vec![];
721 for event in &mut mouse_events {
722 if let Some(lease) = event.wake_lease.take() {
723 if let Some(status) = &default_mouse_device.inspect_status {
724 tracked_leases.push(TrackedWakeLease::new(lease, status.clone()));
725 }
726 }
727 }
728
729 let batch = parse_fidl_mouse_events(mouse_events);
730
731 if let Some(dev_inspect_status) = &default_mouse_device.inspect_status {
732 dev_inspect_status.count_total_received_events(num_received_events);
733 dev_inspect_status.count_total_ignored_events(batch.count_ignored_events);
734 dev_inspect_status.count_total_unexpected_events(batch.count_unexpected_events);
735 dev_inspect_status.count_total_converted_events(batch.count_converted_events);
736 if !batch.events.is_empty() {
737 dev_inspect_status.count_total_generated_events(
738 batch.events.len().try_into().unwrap(),
739 batch.last_event_time_ns.try_into().unwrap(),
740 );
741 }
742 } else {
743 log_warn!("unable to record inspect for mouse device");
744 }
745
746 default_mouse_device.open_files.lock().retain(|f| {
747 let Some(file) = f.upgrade() else {
748 log_warn!("Dropping input file for mouse that failed to upgrade");
749 return false;
750 };
751 match &file.inspect_status {
752 Some(file_inspect_status) => {
753 file_inspect_status.count_received_events(num_received_events);
754 file_inspect_status.count_ignored_events(batch.count_ignored_events);
755 file_inspect_status.count_unexpected_events(batch.count_unexpected_events);
756 file_inspect_status.count_converted_events(batch.count_converted_events);
757 }
758 None => {
759 log_warn!("unable to record inspect within the input file")
760 }
761 }
762 if !batch.events.is_empty() {
763 if let Some(file_inspect_status) = &file.inspect_status {
764 file_inspect_status.count_generated_events(
765 batch.events.len().try_into().unwrap(),
766 batch.last_event_time_ns.try_into().unwrap(),
767 );
768 }
769 file.add_events(batch.events.clone().into_iter().collect());
770 }
771 true
772 });
773 }
774}
775
776fn setup_touch_relay(
777 kernel: &Arc<Kernel>,
778 event_proxy_mode: EventProxyMode,
779 touch_source_client_end: ClientEnd<fuipointer::TouchSourceMarker>,
780 default_touch_device_opened_files: OpenedFiles,
781 device_inspect_status: Option<Arc<InputDeviceStatus>>,
782) -> (DeviceState, ContainerWakingProxy<fuipointer::TouchSourceProxy>) {
783 let touch_counter_name = "touch";
784 let default_touch_device = DeviceState {
785 device_type: InputDeviceType::Touch(FuchsiaTouchEventToLinuxTouchEventConverter::create()),
786 open_files: default_touch_device_opened_files,
787 inspect_status: device_inspect_status,
788 };
789 let (touch_source_proxy, counter) = match event_proxy_mode {
790 EventProxyMode::WakeContainer => {
791 let (touch_source_channel, counter) = create_proxy_for_wake_events_counter(
794 touch_source_client_end.into_channel(),
795 touch_counter_name.to_string(),
796 );
797 (
798 fuipointer::TouchSourceProxy::new(fidl::AsyncChannel::from_channel(
799 touch_source_channel,
800 )),
801 Some(counter),
802 )
803 }
804 EventProxyMode::None => (touch_source_client_end.into_proxy(), None),
805 };
806 (
807 default_touch_device,
808 ContainerWakingProxy::new(
809 kernel.suspend_resume_manager.add_message_counter(touch_counter_name, counter),
810 touch_source_proxy,
811 ),
812 )
813}
814
815fn setup_keyboard_relay(
816 keyboard: KeyboardSynchronousProxy,
817 view_ref: fuiviews::ViewRef,
818 default_keyboard_device_opened_files: OpenedFiles,
819 device_inspect_status: Option<Arc<InputDeviceStatus>>,
820) -> (DeviceState, KeyboardListenerRequestStream) {
821 let default_keyboard_device = DeviceState {
822 device_type: InputDeviceType::Keyboard,
823 open_files: default_keyboard_device_opened_files,
824 inspect_status: device_inspect_status,
825 };
826 let (keyboard_listener, event_stream) =
827 fidl::endpoints::create_request_stream::<KeyboardListenerMarker>();
828 if keyboard.add_listener(view_ref, keyboard_listener, zx::MonotonicInstant::INFINITE).is_err() {
829 log_warn!("Could not register keyboard listener");
830 }
831
832 (default_keyboard_device, event_stream)
833}
834
835fn setup_button_relay(
836 kernel: &Arc<Kernel>,
837 registry_proxy: fuipolicy::DeviceListenerRegistrySynchronousProxy,
838 event_proxy_mode: EventProxyMode,
839 default_keyboard_device_opened_files: OpenedFiles,
840 device_inspect_status: Option<Arc<InputDeviceStatus>>,
841) -> (
842 DeviceState,
843 ContainerWakingStream<fuipolicy::MediaButtonsListenerRequestStream>,
844 ContainerWakingStream<fuipolicy::TouchButtonsListenerRequestStream>,
845) {
846 let default_keyboard_device = DeviceState {
847 device_type: InputDeviceType::Keyboard,
848 open_files: default_keyboard_device_opened_files,
849 inspect_status: device_inspect_status,
850 };
851 let media_buttons_name = "media buttons";
852 let touch_buttons_name = "touch buttons";
853
854 let (remote_media_button_client, remote_media_button_server) =
855 fidl::endpoints::create_endpoints::<fuipolicy::MediaButtonsListenerMarker>();
856 if let Err(e) =
857 registry_proxy.register_listener(remote_media_button_client, zx::MonotonicInstant::INFINITE)
858 {
859 log_warn!("Failed to register media buttons listener: {:?}", e);
860 }
861
862 let (remote_touch_button_client, remote_touch_button_server) =
863 fidl::endpoints::create_endpoints::<fuipolicy::TouchButtonsListenerMarker>();
864 if let Err(e) = registry_proxy
865 .register_touch_buttons_listener(remote_touch_button_client, zx::MonotonicInstant::INFINITE)
866 {
867 log_warn!("Failed to register touch buttons listener: {:?}", e);
868 }
869
870 let (
871 local_media_buttons_listener_stream,
872 media_buttons_counter,
873 local_touch_buttons_listener_stream,
874 touch_buttons_counter,
875 ) = match event_proxy_mode {
876 EventProxyMode::WakeContainer => {
877 let (local_media_buttons_channel, media_buttons_counter) =
878 create_proxy_for_wake_events_counter(
879 remote_media_button_server.into_channel(),
880 media_buttons_name.to_string(),
881 );
882 let local_media_buttons_listener_stream =
883 fuipolicy::MediaButtonsListenerRequestStream::from_channel(
884 fidl::AsyncChannel::from_channel(local_media_buttons_channel),
885 );
886
887 let (local_touch_buttons_channel, touch_buttons_counter) =
888 create_proxy_for_wake_events_counter(
889 remote_touch_button_server.into_channel(),
890 touch_buttons_name.to_string(),
891 );
892 let local_touch_buttons_listener_stream =
893 fuipolicy::TouchButtonsListenerRequestStream::from_channel(
894 fidl::AsyncChannel::from_channel(local_touch_buttons_channel),
895 );
896 (
897 local_media_buttons_listener_stream,
898 Some(media_buttons_counter),
899 local_touch_buttons_listener_stream,
900 Some(touch_buttons_counter),
901 )
902 }
903 EventProxyMode::None => (
904 remote_media_button_server.into_stream(),
905 None,
906 remote_touch_button_server.into_stream(),
907 None,
908 ),
909 };
910
911 (
912 default_keyboard_device,
913 ContainerWakingStream::new(
914 kernel
915 .suspend_resume_manager
916 .add_message_counter(media_buttons_name, media_buttons_counter),
917 local_media_buttons_listener_stream,
918 ),
919 ContainerWakingStream::new(
920 kernel
921 .suspend_resume_manager
922 .add_message_counter(touch_buttons_name, touch_buttons_counter),
923 local_touch_buttons_listener_stream,
924 ),
925 )
926}
927
928fn setup_mouse_relay(
929 kernel: &Arc<Kernel>,
930 event_proxy_mode: EventProxyMode,
931 mouse_source_client_end: ClientEnd<fuipointer::MouseSourceMarker>,
932 default_mouse_device_opened_files: OpenedFiles,
933 device_inspect_status: Option<Arc<InputDeviceStatus>>,
934) -> (DeviceState, ContainerWakingProxy<fuipointer::MouseSourceProxy>) {
935 let mouse_counter_name = "mouse";
936 let default_mouse_device = DeviceState {
937 device_type: InputDeviceType::Mouse,
938 open_files: default_mouse_device_opened_files,
939 inspect_status: device_inspect_status,
940 };
941 let (mouse_source_proxy, counter) = match event_proxy_mode {
942 EventProxyMode::WakeContainer => {
943 let (mouse_source_channel, resume_event) = create_proxy_for_wake_events_counter(
946 mouse_source_client_end.into_channel(),
947 "mouse".to_string(),
948 );
949 (
950 fuipointer::MouseSourceProxy::new(fidl::AsyncChannel::from_channel(
951 mouse_source_channel,
952 )),
953 Some(resume_event),
954 )
955 }
956 EventProxyMode::None => (mouse_source_client_end.into_proxy(), None),
957 };
958
959 (
960 default_mouse_device,
961 ContainerWakingProxy::new(
962 kernel.suspend_resume_manager.add_message_counter(mouse_counter_name, counter),
963 mouse_source_proxy,
964 ),
965 )
966}
967
968fn make_response_for_fidl_event(fidl_event: &FidlTouchEvent) -> FidlTouchResponse {
970 match fidl_event {
971 FidlTouchEvent { pointer_sample: Some(_), .. } => FidlTouchResponse {
972 response_type: Some(TouchResponseType::Yes), trace_flow_id: fidl_event.trace_flow_id,
974 ..Default::default()
975 },
976 _ => FidlTouchResponse::default(),
977 }
978}
979
980fn group_touch_events_by_device_id(
981 events: Vec<FidlTouchEvent>,
982) -> (SortedVecMap<DeviceId, Vec<FidlTouchEvent>>, u64) {
983 let mut events_by_device: SortedVecMap<u32, Vec<FidlTouchEvent>> = SortedVecMap::new();
984 let mut ignored_events: u64 = 0;
985 for e in events {
986 match e {
987 FidlTouchEvent {
988 pointer_sample: Some(TouchPointerSample { interaction: Some(id), .. }),
989 ..
990 } => {
991 if let Some(vec) = events_by_device.get_mut(&id.device_id) {
992 vec.push(e);
993 } else {
994 events_by_device.insert(id.device_id, vec![e]);
995 }
996 }
997 _ => {
998 ignored_events += 1;
999 }
1000 }
1001 }
1002
1003 (events_by_device, ignored_events)
1004}
1005
1006#[cfg(test)]
1007pub async fn start_input_relays_for_test(
1008 locked: &mut starnix_sync::Locked<starnix_sync::Unlocked>,
1009 current_task: &starnix_core::task::CurrentTask,
1010 event_proxy_mode: EventProxyMode,
1011) -> (
1012 Arc<InputEventsRelayHandle>,
1013 crate::InputDevice,
1014 crate::InputDevice,
1015 crate::InputDevice,
1016 starnix_core::vfs::FileHandle,
1017 starnix_core::vfs::FileHandle,
1018 starnix_core::vfs::FileHandle,
1019 fuipointer::TouchSourceRequestStream,
1020 fuipointer::MouseSourceRequestStream,
1021 fidl_fuchsia_ui_input3::KeyboardListenerProxy,
1022 fuipolicy::MediaButtonsListenerProxy,
1023 fuipolicy::TouchButtonsListenerProxy,
1024) {
1025 let inspector = fuchsia_inspect::Inspector::default();
1026
1027 let touch_device = crate::InputDevice::new_touch(700, 1200, inspector.root());
1028 let touch_file =
1029 touch_device.open_test(locked, current_task).expect("Failed to create input file");
1030
1031 let keyboard_device = crate::InputDevice::new_keyboard(inspector.root());
1032 let keyboard_file =
1033 keyboard_device.open_test(locked, current_task).expect("Failed to create input file");
1034
1035 let mouse_device = crate::InputDevice::new_mouse(inspector.root());
1036 let mouse_file =
1037 mouse_device.open_test(locked, current_task).expect("Failed to create input file");
1038
1039 let (touch_source_client_end, touch_source_stream) =
1040 fidl::endpoints::create_request_stream::<fuipointer::TouchSourceMarker>();
1041 let (mouse_source_client_end, mouse_stream) =
1042 fidl::endpoints::create_request_stream::<fuipointer::MouseSourceMarker>();
1043 let (keyboard_proxy, mut keyboard_stream) =
1044 fidl::endpoints::create_sync_proxy_and_stream::<fidl_fuchsia_ui_input3::KeyboardMarker>();
1045 let view_ref_pair = fuchsia_scenic::ViewRefPair::new().expect("Failed to create ViewRefPair");
1046 let (device_registry_proxy, mut device_listener_stream) =
1047 fidl::endpoints::create_sync_proxy_and_stream::<fuipolicy::DeviceListenerRegistryMarker>();
1048
1049 let (relay, relay_handle) = new_input_relay();
1050 relay.start_relays(
1051 ¤t_task.kernel(),
1052 event_proxy_mode,
1053 touch_source_client_end,
1054 keyboard_proxy,
1055 mouse_source_client_end,
1056 view_ref_pair.view_ref,
1057 device_registry_proxy,
1058 touch_device.open_files.clone(),
1059 keyboard_device.open_files.clone(),
1060 mouse_device.open_files.clone(),
1061 Some(touch_device.inspect_status.clone()),
1062 Some(keyboard_device.inspect_status.clone()),
1063 Some(mouse_device.inspect_status.clone()),
1064 );
1065
1066 let keyboard_listener = match keyboard_stream.next().await {
1067 Some(Ok(fidl_fuchsia_ui_input3::KeyboardRequest::AddListener {
1068 view_ref: _,
1069 listener,
1070 responder,
1071 })) => {
1072 let _ = responder.send();
1073 listener.into_proxy()
1074 }
1075 _ => {
1076 panic!("Failed to get event");
1077 }
1078 };
1079
1080 let media_buttons_listener = match device_listener_stream.next().await {
1081 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterListener {
1082 listener,
1083 responder,
1084 })) => {
1085 let _ = responder.send();
1086 listener.into_proxy()
1087 }
1088 _ => {
1089 panic!("Failed to get event");
1090 }
1091 };
1092
1093 let touch_buttons_listener = match device_listener_stream.next().await {
1094 Some(Ok(fuipolicy::DeviceListenerRegistryRequest::RegisterTouchButtonsListener {
1095 listener,
1096 responder,
1097 })) => {
1098 let _ = responder.send();
1099 listener.into_proxy()
1100 }
1101 _ => {
1102 panic!("Failed to get event");
1103 }
1104 };
1105
1106 (
1107 relay_handle,
1108 touch_device,
1109 keyboard_device,
1110 mouse_device,
1111 touch_file,
1112 keyboard_file,
1113 mouse_file,
1114 touch_source_stream,
1115 mouse_stream,
1116 keyboard_listener,
1117 media_buttons_listener,
1118 touch_buttons_listener,
1119 )
1120}
1121
1122#[cfg(test)]
1123mod test {
1124 use super::*;
1125 use anyhow::anyhow;
1126 use fidl_fuchsia_ui_input::{
1127 MediaButtonsEvent, TouchButton, TouchButtonsEvent, TouchDeviceInfo,
1128 };
1129 use fidl_fuchsia_ui_input3 as fuiinput;
1130 use fuipointer::{
1131 EventPhase, MouseEvent, MousePointerSample, TouchEvent, TouchInteractionId,
1132 TouchPointerSample, TouchResponse, TouchSourceRequest, TouchSourceRequestStream,
1133 };
1134 use starnix_core::task::CurrentTask;
1135 #[allow(deprecated, reason = "pre-existing usage")]
1136 use starnix_core::testing::create_kernel_task_and_unlocked;
1137 use starnix_core::vfs::{FileHandle, FileObject, VecOutputBuffer};
1138 use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Unlocked};
1139 use starnix_types::time::timeval_from_time;
1140 use starnix_uapi::errors::{EAGAIN, Errno};
1141 use starnix_uapi::input_id;
1142 use starnix_uapi::open_flags::OpenFlags;
1143 use zerocopy::FromBytes as _;
1144
1145 const INPUT_EVENT_SIZE: usize = std::mem::size_of::<uapi::input_event>();
1146
1147 async fn answer_next_touch_watch_request(
1150 request_stream: &mut TouchSourceRequestStream,
1151 touch_events: Vec<TouchEvent>,
1152 ) -> Vec<TouchResponse> {
1153 match request_stream.next().await {
1154 Some(Ok(TouchSourceRequest::Watch { responses, responder })) => {
1155 responder.send(touch_events).expect("failure sending Watch reply");
1156 responses
1157 }
1158 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1159 }
1160 }
1161
1162 async fn answer_next_mouse_watch_request(
1165 request_stream: &mut fuipointer::MouseSourceRequestStream,
1166 mouse_events: Vec<MouseEvent>,
1167 ) {
1168 match request_stream.next().await {
1169 Some(Ok(fuipointer::MouseSourceRequest::Watch { responder })) => {
1170 responder.send(mouse_events).expect("failure sending Watch reply");
1171 }
1172 unexpected_request => panic!("unexpected request {:?}", unexpected_request),
1173 }
1174 }
1175
1176 fn make_empty_touch_event(device_id: u32) -> TouchEvent {
1177 TouchEvent {
1178 pointer_sample: Some(TouchPointerSample {
1179 interaction: Some(TouchInteractionId {
1180 pointer_id: 0,
1181 device_id,
1182 interaction_id: 0,
1183 }),
1184 ..Default::default()
1185 }),
1186 ..Default::default()
1187 }
1188 }
1189
1190 fn make_touch_event_with_phase_device_id(
1191 phase: EventPhase,
1192 pointer_id: u32,
1193 device_id: u32,
1194 ) -> TouchEvent {
1195 make_touch_event_with_phase_device_id_position(phase, pointer_id, device_id, 0.0, 0.0)
1196 }
1197
1198 fn make_touch_event_with_phase_device_id_position(
1199 phase: EventPhase,
1200 pointer_id: u32,
1201 device_id: u32,
1202 x: f32,
1203 y: f32,
1204 ) -> TouchEvent {
1205 TouchEvent {
1206 timestamp: Some(0),
1207 pointer_sample: Some(TouchPointerSample {
1208 position_in_viewport: Some([x, y]),
1209 phase: Some(phase),
1210 interaction: Some(TouchInteractionId { pointer_id, device_id, interaction_id: 0 }),
1211 ..Default::default()
1212 }),
1213 ..Default::default()
1214 }
1215 }
1216
1217 fn make_mouse_wheel_event(scroll_v_ticks: i64, device_id: u32) -> MouseEvent {
1218 MouseEvent {
1219 timestamp: Some(0),
1220 pointer_sample: Some(MousePointerSample {
1221 device_id: Some(device_id),
1222 scroll_v: Some(scroll_v_ticks),
1223 ..Default::default()
1224 }),
1225 ..Default::default()
1226 }
1227 }
1228
1229 fn read_uapi_events<L>(
1230 locked: &mut Locked<L>,
1231 file: &FileHandle,
1232 current_task: &CurrentTask,
1233 ) -> Vec<uapi::input_event>
1234 where
1235 L: LockEqualOrBefore<FileOpsCore>,
1236 {
1237 std::iter::from_fn(|| {
1238 let locked = locked.cast_locked::<FileOpsCore>();
1239 let mut event_bytes = VecOutputBuffer::new(INPUT_EVENT_SIZE);
1240 match file.read(locked, current_task, &mut event_bytes) {
1241 Ok(INPUT_EVENT_SIZE) => Some(
1242 uapi::input_event::read_from_bytes(Vec::from(event_bytes).as_slice())
1243 .map_err(|_| anyhow!("failed to read input_event from buffer")),
1244 ),
1245 Ok(other_size) => {
1246 Some(Err(anyhow!("got {} bytes (expected {})", other_size, INPUT_EVENT_SIZE)))
1247 }
1248 Err(Errno { code: EAGAIN, .. }) => None,
1249 Err(other_error) => Some(Err(anyhow!("read failed: {:?}", other_error))),
1250 }
1251 })
1252 .enumerate()
1253 .map(|(i, read_res)| match read_res {
1254 Ok(event) => event,
1255 Err(e) => panic!("unexpected result {:?} on iteration {}", e, i),
1256 })
1257 .collect()
1258 }
1259
1260 fn create_test_touch_device(
1261 locked: &mut Locked<Unlocked>,
1262 current_task: &CurrentTask,
1263 input_relay: Arc<InputEventsRelayHandle>,
1264 device_id: u32,
1265 ) -> FileHandle {
1266 let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1267 input_relay.add_touch_device(device_id, open_files.clone(), None);
1268 let inspector = fuchsia_inspect::Inspector::default();
1269 let device_file = Arc::new(InputFile::new_touch(
1270 input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1271 1000,
1272 1000,
1273 inspector.root(),
1274 ));
1275 open_files.lock().push(Arc::downgrade(&device_file));
1276
1277 let root_namespace_node = current_task
1278 .lookup_path_from_root(locked, ".".into())
1279 .expect("failed to get namespace node for root");
1280
1281 FileObject::new(
1282 locked,
1283 ¤t_task,
1284 Box::new(crate::input_file::ArcInputFile(device_file)),
1285 root_namespace_node,
1286 OpenFlags::empty(),
1287 )
1288 .expect("FileObject::new failed")
1289 }
1290
1291 fn make_uapi_input_event(ty: u32, code: u32, value: i32) -> uapi::input_event {
1292 uapi::input_event {
1293 time: timeval_from_time(zx::MonotonicInstant::from_nanos(0)),
1294 type_: ty as u16,
1295 code: code as u16,
1296 value,
1297 }
1298 }
1299
1300 #[::fuchsia::test]
1301 async fn route_touch_event_by_device_id() {
1302 #[allow(deprecated, reason = "pre-existing usage")]
1304 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1305 let (
1306 input_relay,
1307 _touch_device,
1308 _keyboard_device,
1309 _mouse_device,
1310 input_file,
1311 _keyboard_file,
1312 _mouse_file,
1313 mut touch_source_stream,
1314 _mouse_source_stream,
1315 _keyboard_listener,
1316 _media_buttons_listener,
1317 _touch_buttons_listener,
1318 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1319
1320 const DEVICE_ID: u32 = 10;
1321
1322 answer_next_touch_watch_request(
1323 &mut touch_source_stream,
1324 vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1325 )
1326 .await;
1327
1328 answer_next_touch_watch_request(
1332 &mut touch_source_stream,
1333 vec![make_empty_touch_event(DEVICE_ID)],
1334 )
1335 .await;
1336
1337 let events = read_uapi_events(locked, &input_file, ¤t_task);
1339 assert_ne!(events.len(), 0);
1341
1342 let device_id_10_file =
1344 create_test_touch_device(locked, ¤t_task, input_relay.clone(), DEVICE_ID);
1345
1346 answer_next_touch_watch_request(
1347 &mut touch_source_stream,
1348 vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1349 )
1350 .await;
1351
1352 answer_next_touch_watch_request(
1353 &mut touch_source_stream,
1354 vec![make_empty_touch_event(DEVICE_ID)],
1355 )
1356 .await;
1357
1358 let events = read_uapi_events(locked, &input_file, ¤t_task);
1359 assert_eq!(events.len(), 0);
1361
1362 let events = read_uapi_events(locked, &device_id_10_file, ¤t_task);
1363 assert_ne!(events.len(), 0);
1365 }
1366
1367 #[::fuchsia::test]
1368 async fn route_touch_event_with_wake_lease() {
1369 #[allow(deprecated, reason = "pre-existing usage")]
1370 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1371 let (
1372 _input_relay,
1373 touch_device,
1374 _keyboard_device,
1375 _mouse_device,
1376 _input_file,
1377 _keyboard_file,
1378 _mouse_file,
1379 mut touch_source_stream,
1380 _mouse_source_stream,
1381 _keyboard_listener,
1382 _media_buttons_listener,
1383 _touch_buttons_listener,
1384 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1385
1386 const DEVICE_ID: u32 = 10;
1387 let mut event = make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID);
1388 let (p1, _p2) = fidl::EventPair::create();
1389 event.wake_lease = Some(p1);
1390
1391 answer_next_touch_watch_request(&mut touch_source_stream, vec![event]).await;
1392
1393 answer_next_touch_watch_request(
1395 &mut touch_source_stream,
1396 vec![make_empty_touch_event(DEVICE_ID)],
1397 )
1398 .await;
1399
1400 let status = &touch_device.inspect_status;
1401 assert_eq!(
1402 status.total_events_with_wake_lease_count.load(std::sync::atomic::Ordering::Relaxed),
1403 1
1404 );
1405 assert_eq!(status.active_wake_leases_count.load(std::sync::atomic::Ordering::Relaxed), 0);
1406 }
1407
1408 #[::fuchsia::test]
1409 async fn route_touch_event_by_device_id_multi_device_events_in_one_sequence() {
1410 #[allow(deprecated, reason = "pre-existing usage")]
1411 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1412 let (
1413 input_relay,
1414 _touch_device,
1415 _keyboard_device,
1416 _mouse_device,
1417 _input_file,
1418 _keyboard_file,
1419 _mouse_file,
1420 mut touch_source_stream,
1421 _mouse_source_stream,
1422 _keyboard_listener,
1423 _media_buttons_listener,
1424 _touch_buttons_listener,
1425 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1426
1427 const DEVICE_ID_10: u32 = 10;
1428 const DEVICE_ID_11: u32 = 11;
1429
1430 let device_id_10_file =
1431 create_test_touch_device(locked, ¤t_task, input_relay.clone(), DEVICE_ID_10);
1432
1433 let device_id_11_file =
1434 create_test_touch_device(locked, ¤t_task, input_relay.clone(), DEVICE_ID_11);
1435
1436 answer_next_touch_watch_request(
1438 &mut touch_source_stream,
1439 vec![
1440 make_touch_event_with_phase_device_id_position(
1441 EventPhase::Add,
1442 1,
1443 DEVICE_ID_10,
1444 10.0,
1445 20.0,
1446 ),
1447 make_touch_event_with_phase_device_id_position(
1448 EventPhase::Add,
1449 2,
1450 DEVICE_ID_11,
1451 30.0,
1452 40.0,
1453 ),
1454 ],
1455 )
1456 .await;
1457
1458 answer_next_touch_watch_request(&mut touch_source_stream, vec![]).await;
1459
1460 let events_10 = read_uapi_events(locked, &device_id_10_file, ¤t_task);
1461 let events_11 = read_uapi_events(locked, &device_id_11_file, ¤t_task);
1462 assert_eq!(events_10.len(), events_11.len());
1463
1464 assert_eq!(
1465 events_10,
1466 vec![
1467 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1468 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 1),
1469 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 10),
1470 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 20),
1471 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1472 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1473 ]
1474 );
1475
1476 assert_eq!(
1477 events_11,
1478 vec![
1479 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_SLOT, 0),
1480 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_TRACKING_ID, 2),
1481 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_X, 30),
1482 make_uapi_input_event(uapi::EV_ABS, uapi::ABS_MT_POSITION_Y, 40),
1483 make_uapi_input_event(uapi::EV_KEY, uapi::BTN_TOUCH, 1),
1484 make_uapi_input_event(uapi::EV_SYN, uapi::SYN_REPORT, 0),
1485 ]
1486 );
1487 }
1488
1489 #[::fuchsia::test]
1490 async fn route_key_event_by_device_id() {
1491 #[allow(deprecated, reason = "pre-existing usage")]
1493 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1494 let (
1495 input_relay,
1496 _touch_device,
1497 _keyboard_device,
1498 _mouse_device,
1499 _touch_file,
1500 keyboard_file,
1501 _mouse_file,
1502 _touch_source_stream,
1503 _mouse_source_stream,
1504 keyboard_listener,
1505 _media_buttons_listener,
1506 _touch_buttons_listener,
1507 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1508
1509 const DEVICE_ID: u32 = 10;
1510
1511 let key_event = fuiinput::KeyEvent {
1512 timestamp: Some(0),
1513 type_: Some(fuiinput::KeyEventType::Pressed),
1514 key: Some(fidl_fuchsia_input::Key::A),
1515 device_id: Some(DEVICE_ID),
1516 ..Default::default()
1517 };
1518
1519 let _ = keyboard_listener.on_key_event(&key_event).await;
1520
1521 let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1522 assert_ne!(events.len(), 0);
1524
1525 let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1527 input_relay.add_keyboard_device(DEVICE_ID, open_files.clone(), None);
1528 let inspector = fuchsia_inspect::Inspector::default();
1529 let device_id_10_file = Arc::new(InputFile::new_keyboard(
1530 input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1531 inspector.root(),
1532 ));
1533 open_files.lock().push(Arc::downgrade(&device_id_10_file));
1534 let root_namespace_node = current_task
1535 .lookup_path_from_root(locked, ".".into())
1536 .expect("failed to get namespace node for root");
1537 let device_id_10_file_object = FileObject::new(
1538 locked,
1539 ¤t_task,
1540 Box::new(crate::input_file::ArcInputFile(device_id_10_file)),
1541 root_namespace_node,
1542 OpenFlags::empty(),
1543 )
1544 .expect("FileObject::new failed");
1545
1546 let _ = keyboard_listener.on_key_event(&key_event).await;
1547
1548 let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1549 assert_eq!(events.len(), 0);
1551
1552 let events = read_uapi_events(locked, &device_id_10_file_object, ¤t_task);
1553 assert_ne!(events.len(), 0);
1555
1556 std::mem::drop(keyboard_listener); }
1558
1559 #[::fuchsia::test]
1560 async fn route_media_button_event_by_device_id() {
1561 #[allow(deprecated, reason = "pre-existing usage")]
1563 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1564 let (
1565 input_relay,
1566 _touch_device,
1567 _keyboard_device,
1568 _mouse_device,
1569 _touch_file,
1570 keyboard_file,
1571 _mouse_file,
1572 _touch_source_stream,
1573 _mouse_source_stream,
1574 _keyboard_listener,
1575 media_buttons_listener,
1576 _touch_buttons_listener,
1577 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1578
1579 const DEVICE_ID: u32 = 10;
1580
1581 let power_pressed_event = MediaButtonsEvent {
1582 volume: Some(0),
1583 mic_mute: Some(false),
1584 pause: Some(false),
1585 camera_disable: Some(false),
1586 power: Some(true),
1587 function: Some(false),
1588 device_id: Some(DEVICE_ID),
1589 ..Default::default()
1590 };
1591
1592 let _ = media_buttons_listener.on_event(power_pressed_event).await;
1593
1594 let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1595 assert_ne!(events.len(), 0);
1597
1598 let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1600 input_relay.add_keyboard_device(DEVICE_ID, open_files.clone(), None);
1601 let inspector = fuchsia_inspect::Inspector::default();
1602 let device_id_10_file = Arc::new(InputFile::new_keyboard(
1603 input_id { bustype: 0, vendor: 0, product: 0, version: 0 },
1604 inspector.root(),
1605 ));
1606 open_files.lock().push(Arc::downgrade(&device_id_10_file));
1607 let root_namespace_node = current_task
1608 .lookup_path_from_root(locked, ".".into())
1609 .expect("failed to get namespace node for root");
1610 let device_id_10_file_object = FileObject::new(
1611 locked,
1612 ¤t_task,
1613 Box::new(crate::input_file::ArcInputFile(device_id_10_file)),
1614 root_namespace_node,
1615 OpenFlags::empty(),
1616 )
1617 .expect("FileObject::new failed");
1618
1619 let power_released_event = MediaButtonsEvent {
1620 volume: Some(0),
1621 mic_mute: Some(false),
1622 pause: Some(false),
1623 camera_disable: Some(false),
1624 power: Some(false),
1625 function: Some(false),
1626 device_id: Some(DEVICE_ID),
1627 ..Default::default()
1628 };
1629
1630 let _ = media_buttons_listener.on_event(power_released_event).await;
1631
1632 let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1633 assert_eq!(events.len(), 0);
1635
1636 let events = read_uapi_events(locked, &device_id_10_file_object, ¤t_task);
1637 assert_ne!(events.len(), 0);
1639
1640 std::mem::drop(media_buttons_listener); }
1642
1643 #[::fuchsia::test]
1644 async fn route_touch_button_event_by_device_id() {
1645 #[allow(deprecated, reason = "pre-existing usage")]
1647 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1648 let (
1649 input_relay,
1650 _touch_device,
1651 _keyboard_device,
1652 _mouse_device,
1653 touch_file,
1654 _keyboard_file,
1655 _mouse_file,
1656 _touch_source_stream,
1657 _mouse_source_stream,
1658 _keyboard_listener,
1659 _media_buttons_listener,
1660 touch_buttons_listener,
1661 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1662
1663 const DEVICE_ID: u32 = 10;
1664
1665 let palm_pressed_event: TouchButtonsEvent = TouchButtonsEvent {
1666 pressed_buttons: Some(vec![TouchButton::Palm]),
1667 device_info: Some(TouchDeviceInfo { id: Some(DEVICE_ID), ..Default::default() }),
1668 ..Default::default()
1669 };
1670
1671 let _ = touch_buttons_listener.on_event(palm_pressed_event).await;
1672
1673 let events = read_uapi_events(locked, &touch_file, ¤t_task);
1674 assert_ne!(events.len(), 0);
1676
1677 let open_files: OpenedFiles = Arc::new(Mutex::new(vec![]));
1679 input_relay.add_touch_device(DEVICE_ID, open_files.clone(), None);
1680 let device_id_10_file =
1681 create_test_touch_device(locked, ¤t_task, input_relay.clone(), DEVICE_ID);
1682
1683 let palm_released_event: TouchButtonsEvent = TouchButtonsEvent {
1684 pressed_buttons: Some(vec![]),
1685 device_info: Some(TouchDeviceInfo { id: Some(DEVICE_ID), ..Default::default() }),
1686 ..Default::default()
1687 };
1688
1689 let _ = touch_buttons_listener.on_event(palm_released_event).await;
1690
1691 let events = read_uapi_events(locked, &touch_file, ¤t_task);
1692 assert_eq!(events.len(), 0);
1694
1695 let events = read_uapi_events(locked, &device_id_10_file, ¤t_task);
1696 assert_ne!(events.len(), 0);
1698
1699 std::mem::drop(touch_buttons_listener); }
1701
1702 #[::fuchsia::test]
1703 async fn touch_device_multi_reader() {
1704 #[allow(deprecated, reason = "pre-existing usage")]
1706 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1707 let (
1708 _input_relay,
1709 touch_device,
1710 _keyboard_device,
1711 _mouse_device,
1712 touch_reader1,
1713 _keyboard_file,
1714 _mouse_file,
1715 mut touch_source_stream,
1716 _mouse_source_stream,
1717 _keyboard_listener,
1718 _media_buttons_listener,
1719 _touch_buttons_listener,
1720 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1721
1722 let touch_reader2 =
1723 touch_device.open_test(locked, ¤t_task).expect("Failed to create input file");
1724
1725 const DEVICE_ID: u32 = 10;
1726
1727 answer_next_touch_watch_request(
1728 &mut touch_source_stream,
1729 vec![make_touch_event_with_phase_device_id(EventPhase::Add, 1, DEVICE_ID)],
1730 )
1731 .await;
1732
1733 answer_next_touch_watch_request(
1737 &mut touch_source_stream,
1738 vec![make_empty_touch_event(DEVICE_ID)],
1739 )
1740 .await;
1741
1742 let events_from_reader1 = read_uapi_events(locked, &touch_reader1, ¤t_task);
1744 let events_from_reader2 = read_uapi_events(locked, &touch_reader2, ¤t_task);
1745 assert_ne!(events_from_reader1.len(), 0);
1746 assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1747 }
1748
1749 #[::fuchsia::test]
1750 async fn keyboard_device_multi_reader() {
1751 #[allow(deprecated, reason = "pre-existing usage")]
1753 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1754 let (
1755 _input_relay,
1756 _touch_device,
1757 keyboard_device,
1758 _mouse_device,
1759 _touch_file,
1760 keyboard_reader1,
1761 _mouse_file,
1762 _touch_source_stream,
1763 _mouse_source_stream,
1764 keyboard_listener,
1765 _media_buttons_listener,
1766 _touch_buttons_listener,
1767 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1768
1769 let keyboard_reader2 =
1770 keyboard_device.open_test(locked, ¤t_task).expect("Failed to create input file");
1771
1772 const DEVICE_ID: u32 = 10;
1773
1774 let key_event = fuiinput::KeyEvent {
1775 timestamp: Some(0),
1776 type_: Some(fuiinput::KeyEventType::Pressed),
1777 key: Some(fidl_fuchsia_input::Key::A),
1778 device_id: Some(DEVICE_ID),
1779 ..Default::default()
1780 };
1781
1782 let _ = keyboard_listener.on_key_event(&key_event).await;
1783
1784 let events_from_reader1 = read_uapi_events(locked, &keyboard_reader1, ¤t_task);
1786 let events_from_reader2 = read_uapi_events(locked, &keyboard_reader2, ¤t_task);
1787 assert_ne!(events_from_reader1.len(), 0);
1788 assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1789 }
1790
1791 #[::fuchsia::test]
1792 async fn button_device_multi_reader() {
1793 #[allow(deprecated, reason = "pre-existing usage")]
1795 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1796 let (
1797 _input_relay,
1798 _touch_device,
1799 keyboard_device,
1800 _mouse_device,
1801 _touch_file,
1802 keyboard_reader1,
1803 _mouse_file,
1804 _touch_source_stream,
1805 _mouse_source_stream,
1806 _keyboard_listener,
1807 media_buttons_listener,
1808 _touch_buttons_listener,
1809 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1810
1811 let keyboard_reader2 =
1812 keyboard_device.open_test(locked, ¤t_task).expect("Failed to create input file");
1813
1814 const DEVICE_ID: u32 = 10;
1815
1816 let power_pressed_event = MediaButtonsEvent {
1817 volume: Some(0),
1818 mic_mute: Some(false),
1819 pause: Some(false),
1820 camera_disable: Some(false),
1821 power: Some(true),
1822 function: Some(false),
1823 device_id: Some(DEVICE_ID),
1824 ..Default::default()
1825 };
1826
1827 let _ = media_buttons_listener.on_event(power_pressed_event).await;
1828
1829 let events_from_reader1 = read_uapi_events(locked, &keyboard_reader1, ¤t_task);
1831 let events_from_reader2 = read_uapi_events(locked, &keyboard_reader2, ¤t_task);
1832 assert_ne!(events_from_reader1.len(), 0);
1833 assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1834 }
1835
1836 #[::fuchsia::test]
1837 async fn mouse_device_multi_reader() {
1838 #[allow(deprecated, reason = "pre-existing usage")]
1840 let (_kernel, current_task, locked) = create_kernel_task_and_unlocked();
1841 let (
1842 _input_relay,
1843 _touch_device,
1844 _keyboard_device,
1845 mouse_device,
1846 _touch_file,
1847 _keyboard_file,
1848 mouse_reader1,
1849 _touch_stream,
1850 mut mouse_stream,
1851 _keyboard_listener,
1852 _media_buttons_listener,
1853 _touch_buttons_listener,
1854 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::None).await;
1855
1856 let mouse_reader2 =
1857 mouse_device.open_test(locked, ¤t_task).expect("Failed to create input file");
1858
1859 const DEVICE_ID: u32 = 10;
1860
1861 answer_next_mouse_watch_request(
1862 &mut mouse_stream,
1863 vec![make_mouse_wheel_event(1, DEVICE_ID)],
1864 )
1865 .await;
1866
1867 answer_next_mouse_watch_request(
1871 &mut mouse_stream,
1872 vec![make_mouse_wheel_event(0, DEVICE_ID)],
1873 )
1874 .await;
1875
1876 let events_from_reader1 = read_uapi_events(locked, &mouse_reader1, ¤t_task);
1878 let events_from_reader2 = read_uapi_events(locked, &mouse_reader2, ¤t_task);
1879 assert_ne!(events_from_reader1.len(), 0);
1880 assert_eq!(events_from_reader1.len(), events_from_reader2.len());
1881 }
1882
1883 #[::fuchsia::test]
1884 async fn input_message_counters() {
1885 #[allow(deprecated, reason = "pre-existing usage")]
1887 let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
1888 let (
1889 _input_relay,
1890 _touch_device,
1891 _keyboard_device,
1892 _mouse_device,
1893 _touch_file,
1894 keyboard_file,
1895 _mouse_file,
1896 _touch_source_stream,
1897 _mouse_source_stream,
1898 keyboard_listener,
1899 _media_buttons_listener,
1900 _touch_buttons_listener,
1901 ) = start_input_relays_for_test(locked, ¤t_task, EventProxyMode::WakeContainer).await;
1902
1903 const DEVICE_ID: u32 = 10;
1904
1905 let key_event = fuiinput::KeyEvent {
1906 timestamp: Some(0),
1907 type_: Some(fuiinput::KeyEventType::Pressed),
1908 key: Some(fidl_fuchsia_input::Key::A),
1909 device_id: Some(DEVICE_ID),
1910 ..Default::default()
1911 };
1912
1913 let _ = keyboard_listener.on_key_event(&key_event).await;
1914
1915 let events = read_uapi_events(locked, &keyboard_file, ¤t_task);
1916 assert_ne!(events.len(), 0);
1917
1918 assert!(!kernel.suspend_resume_manager.has_nonzero_message_counter());
1919 }
1920}