1use crate::input_device::{Handled, InputDeviceEvent, InputDeviceType, InputEvent, InputEventType};
6use crate::input_handler::InputHandler;
7use async_trait::async_trait;
8use fuchsia_inspect::health::Reporter;
9use fuchsia_inspect::{
10 self as inspect, ExponentialHistogramParams, HistogramProperty, Inspector, NumericProperty,
11 Property,
12};
13
14use futures::FutureExt;
15use futures::lock::Mutex;
16use inspect::Node;
17use std::cell::RefCell;
18use std::collections::{HashMap, HashSet, VecDeque};
19use std::fmt::Debug;
20use std::rc::Rc;
21use std::sync::Arc;
22
23const MAX_RECENT_EVENT_LOG_SIZE: usize = 125;
24const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
25 floor: 0,
26 initial_step: 1,
27 step_multiplier: 10,
28 buckets: 7,
39};
40
41#[derive(Debug, Hash, PartialEq, Eq)]
42enum EventType {
43 Keyboard,
44 LightSensor,
45 ConsumerControls,
46 Mouse,
47 TouchScreen,
48 Touchpad,
49 #[cfg(test)]
50 Fake,
51}
52
53impl std::fmt::Display for EventType {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match &*self {
56 EventType::Keyboard => write!(f, "keyboard"),
57 EventType::LightSensor => write!(f, "light_sensor"),
58 EventType::ConsumerControls => write!(f, "consumer_controls"),
59 EventType::Mouse => write!(f, "mouse"),
60 EventType::TouchScreen => write!(f, "touch_screen"),
61 EventType::Touchpad => write!(f, "touchpad"),
62 #[cfg(test)]
63 EventType::Fake => write!(f, "fake"),
64 }
65 }
66}
67
68impl EventType {
69 pub fn for_device_event(event: &InputDeviceEvent) -> Self {
71 match event {
72 InputDeviceEvent::Keyboard(_) => EventType::Keyboard,
73 InputDeviceEvent::LightSensor(_) => EventType::LightSensor,
74 InputDeviceEvent::ConsumerControls(_) => EventType::ConsumerControls,
75 InputDeviceEvent::Mouse(_) => EventType::Mouse,
76 InputDeviceEvent::TouchScreen(_) => EventType::TouchScreen,
77 InputDeviceEvent::Touchpad(_) => EventType::Touchpad,
78 #[cfg(test)]
79 InputDeviceEvent::Fake => EventType::Fake,
80 }
81 }
82}
83
84#[derive(Debug)]
85struct EventCounters {
86 _node: inspect::Node,
88 events_count: inspect::UintProperty,
90 handled_events_count: inspect::UintProperty,
92 last_seen_timestamp_ns: inspect::IntProperty,
95 last_generated_timestamp_ns: inspect::IntProperty,
98}
99
100impl EventCounters {
101 fn add_new_into(
102 map: &mut HashMap<EventType, EventCounters>,
103 root: &inspect::Node,
104 event_type: EventType,
105 ) {
106 let node = root.create_child(format!("{}", event_type));
107 let events_count = node.create_uint("events_count", 0);
108 let handled_events_count = node.create_uint("handled_events_count", 0);
109 let last_seen_timestamp_ns = node.create_int("last_seen_timestamp_ns", 0);
110 let last_generated_timestamp_ns = node.create_int("last_generated_timestamp_ns", 0);
111 let new_counters = EventCounters {
112 _node: node,
113 events_count,
114 handled_events_count,
115 last_seen_timestamp_ns,
116 last_generated_timestamp_ns,
117 };
118 map.insert(event_type, new_counters);
119 }
120
121 pub fn count_event(
122 &self,
123 time: zx::MonotonicInstant,
124 event_time: zx::MonotonicInstant,
125 handled: &Handled,
126 ) {
127 self.events_count.add(1);
128 if *handled == Handled::Yes {
129 self.handled_events_count.add(1);
130 }
131 self.last_seen_timestamp_ns.set(time.into_nanos());
132 self.last_generated_timestamp_ns.set(event_time.into_nanos());
133 }
134}
135
136pub(crate) struct CircularBuffer<T> {
137 _size: usize,
139 _events: VecDeque<T>,
141}
142
143pub(crate) trait BufferNode {
144 fn get_name(&self) -> &'static str;
145 fn record_inspect(&self, node: &Node);
146}
147
148impl<T> CircularBuffer<T>
149where
150 T: BufferNode,
151{
152 pub(crate) fn new(size: usize) -> Self {
153 let events = VecDeque::with_capacity(size);
154 CircularBuffer { _size: size, _events: events }
155 }
156
157 pub(crate) fn push(&mut self, event: T) {
158 if self._events.len() >= self._size {
159 std::mem::drop(self._events.pop_front());
160 }
161 self._events.push_back(event);
162 }
163
164 pub(crate) fn record_all_lazy_inspect(
165 &self,
166 inspector: inspect::Inspector,
167 ) -> inspect::Inspector {
168 self._events.iter().enumerate().for_each(|(i, event)| {
169 inspector.root().record_child(format!("{:03}_{}", i, event.get_name()), move |node| {
172 event.record_inspect(node)
173 });
174 });
175 inspector
176 }
177}
178
179impl BufferNode for InputEvent {
180 fn get_name(&self) -> &'static str {
181 self.get_event_type()
182 }
183
184 fn record_inspect(&self, node: &Node) {
185 InputEvent::record_inspect(self, node);
186 }
187}
188
189pub struct InspectHandler<F> {
194 now: RefCell<F>,
196 node: inspect::Node,
198 events_count: inspect::UintProperty,
200 last_seen_timestamp_ns: inspect::IntProperty,
203 last_generated_timestamp_ns: inspect::IntProperty,
206 events_by_type: HashMap<EventType, EventCounters>,
208 recent_events_log: Option<Arc<Mutex<CircularBuffer<InputEvent>>>>,
210 pipeline_latency_ms: inspect::IntExponentialHistogramProperty,
214 health_node: RefCell<fuchsia_inspect::health::Node>,
216}
217
218impl<F: FnMut() -> zx::MonotonicInstant + 'static> Debug for InspectHandler<F> {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 f.debug_struct("InspectHandler")
221 .field("node", &self.node)
222 .field("events_count", &self.events_count)
223 .field("last_seen_timestamp_ns", &self.last_seen_timestamp_ns)
224 .field("last_generated_timestamp_ns", &self.last_generated_timestamp_ns)
225 .field("events_by_type", &self.events_by_type)
226 .field("recent_events_log", &self.recent_events_log)
227 .field("pipeline_latency_ms", &self.pipeline_latency_ms)
228 .finish()
229 }
230}
231
232#[async_trait(?Send)]
233impl<F: FnMut() -> zx::MonotonicInstant + 'static> InputHandler for InspectHandler<F> {
234 async fn handle_input_event(self: Rc<Self>, input_event: InputEvent) -> Vec<InputEvent> {
235 fuchsia_trace::duration!("input", "inspect_handler");
236 let tracing_id = input_event.trace_id.unwrap_or_else(|| 0.into());
237 fuchsia_trace::flow_step!("input", "event_in_input_pipeline", tracing_id);
238
239 let event_time = input_event.event_time;
240 let now = (self.now.borrow_mut())();
241 self.events_count.add(1);
242 self.last_seen_timestamp_ns.set(now.into_nanos());
243 self.last_generated_timestamp_ns.set(event_time.into_nanos());
244 let event_type = EventType::for_device_event(&input_event.device_event);
245 self.events_by_type
246 .get(&event_type)
247 .unwrap_or_else(|| panic!("no event counters for {}", event_type))
248 .count_event(now, event_time, &input_event.handled);
249 if let Some(recent_events_log) = &self.recent_events_log {
250 recent_events_log.lock().await.push(input_event.clone());
251 }
252 self.pipeline_latency_ms.insert((now - event_time).into_millis());
253 vec![input_event]
254 }
255
256 fn set_handler_healthy(self: std::rc::Rc<Self>) {
257 self.health_node.borrow_mut().set_ok();
258 }
259
260 fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
261 self.health_node.borrow_mut().set_unhealthy(msg);
262 }
263
264 fn get_name(&self) -> &'static str {
265 "InspectHandler"
266 }
267
268 fn interest(&self) -> Vec<InputEventType> {
269 vec![
270 InputEventType::Keyboard,
271 InputEventType::LightSensor,
272 InputEventType::ConsumerControls,
273 InputEventType::Mouse,
274 InputEventType::TouchScreen,
275 InputEventType::Touchpad,
276 #[cfg(test)]
277 InputEventType::Fake,
278 ]
279 }
280}
281
282pub fn make_inspect_handler(
286 node: inspect::Node,
287 supported_input_devices: &HashSet<&InputDeviceType>,
288 displays_recent_events: bool,
289) -> Rc<InspectHandler<fn() -> zx::MonotonicInstant>> {
290 InspectHandler::new_internal(
291 node,
292 zx::MonotonicInstant::get,
293 supported_input_devices,
294 displays_recent_events,
295 )
296}
297
298impl<F> InspectHandler<F> {
299 fn new_internal(
302 node: inspect::Node,
303 now: F,
304 supported_input_devices: &HashSet<&InputDeviceType>,
305 displays_recent_events: bool,
306 ) -> Rc<Self> {
307 let event_count = node.create_uint("events_count", 0);
308 let last_seen_timestamp_ns = node.create_int("last_seen_timestamp_ns", 0);
309 let last_generated_timestamp_ns = node.create_int("last_generated_timestamp_ns", 0);
310
311 let recent_events_log = match displays_recent_events {
312 true => {
313 let recent_events =
314 Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
315 record_lazy_recent_events(&node, Arc::clone(&recent_events));
316 Some(recent_events)
317 }
318 false => None,
319 };
320
321 let pipeline_latency_ms = node
322 .create_int_exponential_histogram("pipeline_latency_ms", LATENCY_HISTOGRAM_PROPERTIES);
323
324 let mut health_node = fuchsia_inspect::health::Node::new(&node);
325 health_node.set_starting_up();
326
327 let mut events_by_type = HashMap::new();
328 if supported_input_devices.contains(&InputDeviceType::Keyboard) {
329 EventCounters::add_new_into(&mut events_by_type, &node, EventType::Keyboard);
330 }
331 if supported_input_devices.contains(&InputDeviceType::ConsumerControls) {
332 EventCounters::add_new_into(&mut events_by_type, &node, EventType::ConsumerControls);
333 }
334 if supported_input_devices.contains(&InputDeviceType::LightSensor) {
335 EventCounters::add_new_into(&mut events_by_type, &node, EventType::LightSensor);
336 }
337 if supported_input_devices.contains(&InputDeviceType::Mouse) {
338 EventCounters::add_new_into(&mut events_by_type, &node, EventType::Mouse);
339 }
340 if supported_input_devices.contains(&InputDeviceType::Touch) {
341 EventCounters::add_new_into(&mut events_by_type, &node, EventType::TouchScreen);
342 EventCounters::add_new_into(&mut events_by_type, &node, EventType::Touchpad);
343 }
344 #[cfg(test)]
345 EventCounters::add_new_into(&mut events_by_type, &node, EventType::Fake);
346
347 Rc::new(Self {
348 now: RefCell::new(now),
349 node,
350 events_count: event_count,
351 last_seen_timestamp_ns,
352 last_generated_timestamp_ns,
353 events_by_type,
354 recent_events_log,
355 pipeline_latency_ms,
356 health_node: RefCell::new(health_node),
357 })
358 }
359}
360
361fn record_lazy_recent_events(
362 node: &inspect::Node,
363 recent_events: Arc<Mutex<CircularBuffer<InputEvent>>>,
364) {
365 node.record_lazy_child("recent_events_log", move || {
366 let recent_events_clone = Arc::clone(&recent_events);
367 async move {
368 let inspector = Inspector::default();
369 Ok(recent_events_clone.lock().await.record_all_lazy_inspect(inspector))
370 }
371 .boxed()
372 });
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use crate::input_device::{self, InputDeviceDescriptor};
379 use crate::keyboard_binding::KeyboardDeviceDescriptor;
380 use crate::light_sensor::types::Rgbc;
381 use crate::light_sensor_binding::{LightSensorDeviceDescriptor, LightSensorEvent};
382 use crate::mouse_binding::{
383 MouseDeviceDescriptor, MouseLocation, MousePhase, PrecisionScroll, RawWheelDelta,
384 WheelDelta,
385 };
386 use crate::testing_utilities::{
387 consumer_controls_device_descriptor, create_consumer_controls_event,
388 create_fake_handled_input_event, create_fake_input_event, create_keyboard_event,
389 create_mouse_event, create_touch_contact, create_touch_screen_event, create_touchpad_event,
390 };
391 use crate::touch_binding::{TouchScreenDeviceDescriptor, TouchpadDeviceDescriptor};
392 use crate::utils::Position;
393 use diagnostics_assertions::{AnyProperty, assert_data_tree};
394 use fidl::endpoints::create_proxy_and_stream;
395 use fidl_fuchsia_input_report::InputDeviceMarker;
396 use fuchsia_async as fasync;
397 use maplit::{hashmap, hashset};
398 use test_case::test_case;
399
400 fn fixed_now() -> zx::MonotonicInstant {
401 zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_nanos(42)
402 }
403
404 #[fasync::run_singlethreaded(test)]
405 async fn circular_buffer_no_overflow() {
406 let mut circular_buffer = CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE);
407 assert_eq!(circular_buffer._size, MAX_RECENT_EVENT_LOG_SIZE);
408
409 let first_event_time = zx::MonotonicInstant::get();
410 circular_buffer.push(create_fake_input_event(first_event_time));
411 let second_event_time = zx::MonotonicInstant::get();
412 circular_buffer.push(create_fake_input_event(second_event_time));
413
414 for _i in 2..MAX_RECENT_EVENT_LOG_SIZE {
416 let curr_event_time = zx::MonotonicInstant::get();
417 circular_buffer.push(create_fake_input_event(curr_event_time));
418 match circular_buffer._events.back() {
419 Some(event) => assert_eq!(event.event_time, curr_event_time),
420 None => assert!(false),
421 }
422 }
423
424 match circular_buffer._events.front() {
426 Some(event) => assert_eq!(event.event_time, first_event_time),
427 None => assert!(false),
428 }
429
430 let last_event_time = zx::MonotonicInstant::get();
432 circular_buffer.push(create_fake_input_event(last_event_time));
433 match circular_buffer._events.front() {
434 Some(event) => assert_eq!(event.event_time, second_event_time),
435 None => assert!(false),
436 }
437 match circular_buffer._events.back() {
438 Some(event) => assert_eq!(event.event_time, last_event_time),
439 None => assert!(false),
440 }
441 }
442
443 #[fasync::run_singlethreaded(test)]
444 async fn recent_events_log_records_inspect() {
445 let inspector = fuchsia_inspect::Inspector::default();
446
447 let recent_events_log =
448 Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
449 record_lazy_recent_events(inspector.root(), Arc::clone(&recent_events_log));
450
451 let keyboard_descriptor = InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
452 keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
453 ..Default::default()
454 });
455 let mouse_descriptor = InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
456 device_id: 1u32,
457 absolute_x_range: None,
458 absolute_y_range: None,
459 wheel_v_range: None,
460 wheel_h_range: None,
461 buttons: None,
462 counts_per_mm: 12u32,
463 });
464 let touch_screen_descriptor =
465 InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
466 device_id: 1,
467 contacts: vec![],
468 });
469 let touchpad_descriptor = InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
470 device_id: 1,
471 contacts: vec![],
472 });
473
474 let pressed_buttons = HashSet::from([1u8, 21u8, 15u8]);
475 let mut pressed_buttons_vec: Vec<u64> = vec![];
476 pressed_buttons.iter().for_each(|button| {
477 pressed_buttons_vec.push(*button as u64);
478 });
479
480 let (light_sensor_proxy, _) = create_proxy_and_stream::<InputDeviceMarker>();
481
482 let recent_events = vec![
483 create_keyboard_event(
484 fidl_fuchsia_input::Key::A,
485 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
486 None,
487 &keyboard_descriptor,
488 None,
489 ),
490 create_consumer_controls_event(
491 vec![
492 fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
493 fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
494 fidl_fuchsia_input_report::ConsumerControlButton::Pause,
495 fidl_fuchsia_input_report::ConsumerControlButton::VolumeDown,
496 fidl_fuchsia_input_report::ConsumerControlButton::MicMute,
497 fidl_fuchsia_input_report::ConsumerControlButton::CameraDisable,
498 fidl_fuchsia_input_report::ConsumerControlButton::FactoryReset,
499 fidl_fuchsia_input_report::ConsumerControlButton::Reboot,
500 ],
501 zx::MonotonicInstant::get(),
502 &consumer_controls_device_descriptor(),
503 ),
504 create_mouse_event(
505 MouseLocation::Absolute(Position { x: 7.0f32, y: 15.0f32 }),
506 Some(WheelDelta {
507 raw_data: RawWheelDelta::Ticks(5i64),
508 physical_pixel: Some(8.0f32),
509 }),
510 Some(WheelDelta {
511 raw_data: RawWheelDelta::Millimeters(10.0f32),
512 physical_pixel: Some(8.0f32),
513 }),
514 Some(PrecisionScroll::Yes),
515 MousePhase::Move,
516 HashSet::from([1u8]),
517 pressed_buttons.clone(),
518 zx::MonotonicInstant::get(),
519 &mouse_descriptor,
520 ),
521 create_touch_screen_event(
522 hashmap! {
523 fidl_fuchsia_ui_input::PointerEventPhase::Add
524 => vec![create_touch_contact(1u32, Position { x: 10.0, y: 30.0 })],
525 fidl_fuchsia_ui_input::PointerEventPhase::Move
526 => vec![create_touch_contact(1u32, Position { x: 11.0, y: 31.0 })],
527 },
528 zx::MonotonicInstant::get(),
529 &touch_screen_descriptor,
530 ),
531 create_touchpad_event(
532 vec![
533 create_touch_contact(1u32, Position { x: 0.0, y: 0.0 }),
534 create_touch_contact(2u32, Position { x: 10.0, y: 10.0 }),
535 ],
536 HashSet::new(),
537 zx::MonotonicInstant::get(),
538 &touchpad_descriptor,
539 ),
540 InputEvent {
541 device_event: InputDeviceEvent::LightSensor(LightSensorEvent {
542 device_proxy: light_sensor_proxy,
543 rgbc: Rgbc { red: 1, green: 2, blue: 3, clear: 14747 },
544 }),
545 device_descriptor: InputDeviceDescriptor::LightSensor(
546 LightSensorDeviceDescriptor {
547 vendor_id: 1,
548 product_id: 2,
549 device_id: 3,
550 sensor_layout: Rgbc { red: 1, green: 2, blue: 3, clear: 4 },
551 },
552 ),
553 event_time: zx::MonotonicInstant::get(),
554 handled: input_device::Handled::No,
555 trace_id: None,
556 },
557 create_keyboard_event(
558 fidl_fuchsia_input::Key::B,
559 fidl_fuchsia_ui_input3::KeyEventType::Pressed,
560 None,
561 &keyboard_descriptor,
562 None,
563 ),
564 ];
565
566 for event in recent_events.into_iter() {
567 recent_events_log.lock().await.push(event);
568 }
569
570 assert_data_tree!(inspector, root: {
571 recent_events_log: {
572 "000_keyboard_event": {
573 event_time: AnyProperty,
574 },
575 "001_consumer_controls_event": {
576 event_time: AnyProperty,
577 pressed_buttons: vec!["volume_up", "volume_up", "pause", "volume_down", "mic_mute", "camera_disable", "factory_reset", "reboot"],
578 },
579 "002_mouse_event": {
580 event_time: AnyProperty,
581 location_absolute: { x: 7.0f64, y: 15.0f64},
582 wheel_delta_v: {
583 ticks: 5i64,
584 physical_pixel: 8.0f64,
585 },
586 wheel_delta_h: {
587 millimeters: 10.0f64,
588 physical_pixel: 8.0f64,
589 },
590 is_precision_scroll: "yes",
591 phase: "move",
592 affected_buttons: vec![1u64],
593 pressed_buttons: pressed_buttons_vec.clone(),
594 },
595 "003_touch_screen_event": {
596 event_time: AnyProperty,
597 injector_contacts: {
598 add: {
599 "1": {
600 position_x_mm: 10.0f64,
601 position_y_mm: 30.0f64,
602 },
603 },
604 change: {
605 "1": {
606 position_x_mm: 11.0f64,
607 position_y_mm: 31.0f64,
608 },
609 },
610 remove: {},
611 },
612 pressed_buttons: Vec::<String>::new(),
613 },
614 "004_touchpad_event": {
615 event_time: AnyProperty,
616 pressed_buttons: Vec::<u64>::new(),
617 injector_contacts: {
618 "1": {
619 position_x_mm: 0.0f64,
620 position_y_mm: 0.0f64,
621 },
622 "2": {
623 position_x_mm: 10.0f64,
624 position_y_mm: 10.0f64,
625 },
626 },
627 },
628 "005_light_sensor_event": {
629 event_time: AnyProperty,
630 red: 1u64,
631 green: 2u64,
632 blue: 3u64,
633 clear: 14747u64,
634 },
635 "006_keyboard_event": {
636 event_time: AnyProperty,
637 },
638 }
639 });
640 }
641
642 #[fasync::run_singlethreaded(test)]
643 async fn verify_inspect_no_recent_events_log() {
644 let inspector = inspect::Inspector::default();
645 let root = inspector.root();
646 let test_node = root.create_child("test_node");
647 let supported_input_devices: HashSet<&InputDeviceType> = HashSet::from([
648 &input_device::InputDeviceType::Keyboard,
649 &input_device::InputDeviceType::ConsumerControls,
650 &input_device::InputDeviceType::LightSensor,
651 &input_device::InputDeviceType::Mouse,
652 &input_device::InputDeviceType::Touch,
653 ]);
654
655 let handler = super::InspectHandler::new_internal(
656 test_node,
657 fixed_now,
658 &supported_input_devices,
659 false,
660 );
661 assert_data_tree!(inspector, root: {
662 test_node: contains {
663 events_count: 0u64,
664 last_seen_timestamp_ns: 0i64,
665 last_generated_timestamp_ns: 0i64,
666 consumer_controls: {
667 events_count: 0u64,
668 handled_events_count: 0u64,
669 last_generated_timestamp_ns: 0i64,
670 last_seen_timestamp_ns: 0i64,
671 },
672 fake: {
673 events_count: 0u64,
674 handled_events_count: 0u64,
675 last_generated_timestamp_ns: 0i64,
676 last_seen_timestamp_ns: 0i64,
677 },
678 keyboard: {
679 events_count: 0u64,
680 handled_events_count: 0u64,
681 last_generated_timestamp_ns: 0i64,
682 last_seen_timestamp_ns: 0i64,
683 },
684 light_sensor: {
685 events_count: 0u64,
686 handled_events_count: 0u64,
687 last_generated_timestamp_ns: 0i64,
688 last_seen_timestamp_ns: 0i64,
689 },
690 mouse: {
691 events_count: 0u64,
692 handled_events_count: 0u64,
693 last_generated_timestamp_ns: 0i64,
694 last_seen_timestamp_ns: 0i64,
695 },
696 touch_screen: {
697 events_count: 0u64,
698 handled_events_count: 0u64,
699 last_generated_timestamp_ns: 0i64,
700 last_seen_timestamp_ns: 0i64,
701 },
702 touchpad: {
703 events_count: 0u64,
704 handled_events_count: 0u64,
705 last_generated_timestamp_ns: 0i64,
706 last_seen_timestamp_ns: 0i64,
707 },
708 }
709 });
710
711 handler
712 .clone()
713 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
714 .await;
715 assert_data_tree!(inspector, root: {
716 test_node: contains {
717 events_count: 1u64,
718 last_seen_timestamp_ns: 42i64,
719 last_generated_timestamp_ns: 43i64,
720 consumer_controls: {
721 events_count: 0u64,
722 handled_events_count: 0u64,
723 last_generated_timestamp_ns: 0i64,
724 last_seen_timestamp_ns: 0i64,
725 },
726 fake: {
727 events_count: 1u64,
728 handled_events_count: 0u64,
729 last_generated_timestamp_ns: 43i64,
730 last_seen_timestamp_ns: 42i64,
731 },
732 keyboard: {
733 events_count: 0u64,
734 handled_events_count: 0u64,
735 last_generated_timestamp_ns: 0i64,
736 last_seen_timestamp_ns: 0i64,
737 },
738 light_sensor: {
739 events_count: 0u64,
740 handled_events_count: 0u64,
741 last_generated_timestamp_ns: 0i64,
742 last_seen_timestamp_ns: 0i64,
743 },
744 mouse: {
745 events_count: 0u64,
746 handled_events_count: 0u64,
747 last_generated_timestamp_ns: 0i64,
748 last_seen_timestamp_ns: 0i64,
749 },
750 touch_screen: {
751 events_count: 0u64,
752 handled_events_count: 0u64,
753 last_generated_timestamp_ns: 0i64,
754 last_seen_timestamp_ns: 0i64,
755 },
756 touchpad: {
757 events_count: 0u64,
758 handled_events_count: 0u64,
759 last_generated_timestamp_ns: 0i64,
760 last_seen_timestamp_ns: 0i64,
761 },
762 }
763 });
764
765 handler
766 .clone()
767 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
768 .await;
769 assert_data_tree!(inspector, root: {
770 test_node: contains {
771 events_count: 2u64,
772 last_seen_timestamp_ns: 42i64,
773 last_generated_timestamp_ns: 44i64,
774 consumer_controls: {
775 events_count: 0u64,
776 handled_events_count: 0u64,
777 last_generated_timestamp_ns: 0i64,
778 last_seen_timestamp_ns: 0i64,
779 },
780 fake: {
781 events_count: 2u64,
782 handled_events_count: 0u64,
783 last_generated_timestamp_ns: 44i64,
784 last_seen_timestamp_ns: 42i64,
785 },
786 keyboard: {
787 events_count: 0u64,
788 handled_events_count: 0u64,
789 last_generated_timestamp_ns: 0i64,
790 last_seen_timestamp_ns: 0i64,
791 },
792 light_sensor: {
793 events_count: 0u64,
794 handled_events_count: 0u64,
795 last_generated_timestamp_ns: 0i64,
796 last_seen_timestamp_ns: 0i64,
797 },
798 mouse: {
799 events_count: 0u64,
800 handled_events_count: 0u64,
801 last_generated_timestamp_ns: 0i64,
802 last_seen_timestamp_ns: 0i64,
803 },
804 touch_screen: {
805 events_count: 0u64,
806 handled_events_count: 0u64,
807 last_generated_timestamp_ns: 0i64,
808 last_seen_timestamp_ns: 0i64,
809 },
810 touchpad: {
811 events_count: 0u64,
812 handled_events_count: 0u64,
813 last_generated_timestamp_ns: 0i64,
814 last_seen_timestamp_ns: 0i64,
815 },
816 }
817 });
818
819 handler
820 .clone()
821 .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
822 44,
823 )))
824 .await;
825 assert_data_tree!(inspector, root: {
826 test_node: contains {
827 events_count: 3u64,
828 last_seen_timestamp_ns: 42i64,
829 last_generated_timestamp_ns: 44i64,
830 consumer_controls: {
831 events_count: 0u64,
832 handled_events_count: 0u64,
833 last_generated_timestamp_ns: 0i64,
834 last_seen_timestamp_ns: 0i64,
835 },
836 fake: {
837 events_count: 3u64,
838 handled_events_count: 1u64,
839 last_generated_timestamp_ns: 44i64,
840 last_seen_timestamp_ns: 42i64,
841 },
842 keyboard: {
843 events_count: 0u64,
844 handled_events_count: 0u64,
845 last_generated_timestamp_ns: 0i64,
846 last_seen_timestamp_ns: 0i64,
847 },
848 light_sensor: {
849 events_count: 0u64,
850 handled_events_count: 0u64,
851 last_generated_timestamp_ns: 0i64,
852 last_seen_timestamp_ns: 0i64,
853 },
854 mouse: {
855 events_count: 0u64,
856 handled_events_count: 0u64,
857 last_generated_timestamp_ns: 0i64,
858 last_seen_timestamp_ns: 0i64,
859 },
860 touch_screen: {
861 events_count: 0u64,
862 handled_events_count: 0u64,
863 last_generated_timestamp_ns: 0i64,
864 last_seen_timestamp_ns: 0i64,
865 },
866 touchpad: {
867 events_count: 0u64,
868 handled_events_count: 0u64,
869 last_generated_timestamp_ns: 0i64,
870 last_seen_timestamp_ns: 0i64,
871 },
872 }
873 });
874 }
875
876 #[fasync::run_singlethreaded(test)]
877 async fn verify_inspect_with_recent_events_log() {
878 let inspector = inspect::Inspector::default();
879 let root = inspector.root();
880 let test_node = root.create_child("test_node");
881 let supported_input_devices: HashSet<&InputDeviceType> = HashSet::from([
882 &input_device::InputDeviceType::Keyboard,
883 &input_device::InputDeviceType::ConsumerControls,
884 &input_device::InputDeviceType::LightSensor,
885 &input_device::InputDeviceType::Mouse,
886 &input_device::InputDeviceType::Touch,
887 ]);
888
889 let handler = super::InspectHandler::new_internal(
890 test_node,
891 fixed_now,
892 &supported_input_devices,
893 true,
894 );
895 assert_data_tree!(inspector, root: {
896 test_node: contains {
897 events_count: 0u64,
898 last_seen_timestamp_ns: 0i64,
899 last_generated_timestamp_ns: 0i64,
900 recent_events_log: {},
901 consumer_controls: {
902 events_count: 0u64,
903 handled_events_count: 0u64,
904 last_generated_timestamp_ns: 0i64,
905 last_seen_timestamp_ns: 0i64,
906 },
907 fake: {
908 events_count: 0u64,
909 handled_events_count: 0u64,
910 last_generated_timestamp_ns: 0i64,
911 last_seen_timestamp_ns: 0i64,
912 },
913 keyboard: {
914 events_count: 0u64,
915 handled_events_count: 0u64,
916 last_generated_timestamp_ns: 0i64,
917 last_seen_timestamp_ns: 0i64,
918 },
919 light_sensor: {
920 events_count: 0u64,
921 handled_events_count: 0u64,
922 last_generated_timestamp_ns: 0i64,
923 last_seen_timestamp_ns: 0i64,
924 },
925 mouse: {
926 events_count: 0u64,
927 handled_events_count: 0u64,
928 last_generated_timestamp_ns: 0i64,
929 last_seen_timestamp_ns: 0i64,
930 },
931 touch_screen: {
932 events_count: 0u64,
933 handled_events_count: 0u64,
934 last_generated_timestamp_ns: 0i64,
935 last_seen_timestamp_ns: 0i64,
936 },
937 touchpad: {
938 events_count: 0u64,
939 handled_events_count: 0u64,
940 last_generated_timestamp_ns: 0i64,
941 last_seen_timestamp_ns: 0i64,
942 },
943 }
944 });
945
946 handler
947 .clone()
948 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
949 .await;
950 assert_data_tree!(inspector, root: {
951 test_node: contains {
952 events_count: 1u64,
953 last_seen_timestamp_ns: 42i64,
954 last_generated_timestamp_ns: 43i64,
955 recent_events_log: {
956 "000_fake_event": {
957 event_time: 43i64,
958 },
959 },
960 consumer_controls: {
961 events_count: 0u64,
962 handled_events_count: 0u64,
963 last_generated_timestamp_ns: 0i64,
964 last_seen_timestamp_ns: 0i64,
965 },
966 fake: {
967 events_count: 1u64,
968 handled_events_count: 0u64,
969 last_generated_timestamp_ns: 43i64,
970 last_seen_timestamp_ns: 42i64,
971 },
972 keyboard: {
973 events_count: 0u64,
974 handled_events_count: 0u64,
975 last_generated_timestamp_ns: 0i64,
976 last_seen_timestamp_ns: 0i64,
977 },
978 light_sensor: {
979 events_count: 0u64,
980 handled_events_count: 0u64,
981 last_generated_timestamp_ns: 0i64,
982 last_seen_timestamp_ns: 0i64,
983 },
984 mouse: {
985 events_count: 0u64,
986 handled_events_count: 0u64,
987 last_generated_timestamp_ns: 0i64,
988 last_seen_timestamp_ns: 0i64,
989 },
990 touch_screen: {
991 events_count: 0u64,
992 handled_events_count: 0u64,
993 last_generated_timestamp_ns: 0i64,
994 last_seen_timestamp_ns: 0i64,
995 },
996 touchpad: {
997 events_count: 0u64,
998 handled_events_count: 0u64,
999 last_generated_timestamp_ns: 0i64,
1000 last_seen_timestamp_ns: 0i64,
1001 },
1002 }
1003 });
1004
1005 handler
1006 .clone()
1007 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
1008 .await;
1009 assert_data_tree!(inspector, root: {
1010 test_node: contains {
1011 events_count: 2u64,
1012 last_seen_timestamp_ns: 42i64,
1013 last_generated_timestamp_ns: 44i64,
1014 recent_events_log: {
1015 "000_fake_event": {
1016 event_time: 43i64,
1017 },
1018 "001_fake_event": {
1019 event_time: 44i64,
1020 },
1021 },
1022 consumer_controls: {
1023 events_count: 0u64,
1024 handled_events_count: 0u64,
1025 last_generated_timestamp_ns: 0i64,
1026 last_seen_timestamp_ns: 0i64,
1027 },
1028 fake: {
1029 events_count: 2u64,
1030 handled_events_count: 0u64,
1031 last_generated_timestamp_ns: 44i64,
1032 last_seen_timestamp_ns: 42i64,
1033 },
1034 keyboard: {
1035 events_count: 0u64,
1036 handled_events_count: 0u64,
1037 last_generated_timestamp_ns: 0i64,
1038 last_seen_timestamp_ns: 0i64,
1039 },
1040 light_sensor: {
1041 events_count: 0u64,
1042 handled_events_count: 0u64,
1043 last_generated_timestamp_ns: 0i64,
1044 last_seen_timestamp_ns: 0i64,
1045 },
1046 mouse: {
1047 events_count: 0u64,
1048 handled_events_count: 0u64,
1049 last_generated_timestamp_ns: 0i64,
1050 last_seen_timestamp_ns: 0i64,
1051 },
1052 touch_screen: {
1053 events_count: 0u64,
1054 handled_events_count: 0u64,
1055 last_generated_timestamp_ns: 0i64,
1056 last_seen_timestamp_ns: 0i64,
1057 },
1058 touchpad: {
1059 events_count: 0u64,
1060 handled_events_count: 0u64,
1061 last_generated_timestamp_ns: 0i64,
1062 last_seen_timestamp_ns: 0i64,
1063 },
1064 }
1065 });
1066
1067 handler
1068 .clone()
1069 .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
1070 44,
1071 )))
1072 .await;
1073 assert_data_tree!(inspector, root: {
1074 test_node: contains {
1075 events_count: 3u64,
1076 last_seen_timestamp_ns: 42i64,
1077 last_generated_timestamp_ns: 44i64,
1078 recent_events_log: {
1079 "000_fake_event": {
1080 event_time: 43i64,
1081 },
1082 "001_fake_event": {
1083 event_time: 44i64,
1084 },
1085 "002_fake_event": {
1086 event_time: 44i64,
1087 },
1088 },
1089 consumer_controls: {
1090 events_count: 0u64,
1091 handled_events_count: 0u64,
1092 last_generated_timestamp_ns: 0i64,
1093 last_seen_timestamp_ns: 0i64,
1094 },
1095 fake: {
1096 events_count: 3u64,
1097 handled_events_count: 1u64,
1098 last_generated_timestamp_ns: 44i64,
1099 last_seen_timestamp_ns: 42i64,
1100 },
1101 keyboard: {
1102 events_count: 0u64,
1103 handled_events_count: 0u64,
1104 last_generated_timestamp_ns: 0i64,
1105 last_seen_timestamp_ns: 0i64,
1106 },
1107 light_sensor: {
1108 events_count: 0u64,
1109 handled_events_count: 0u64,
1110 last_generated_timestamp_ns: 0i64,
1111 last_seen_timestamp_ns: 0i64,
1112 },
1113 mouse: {
1114 events_count: 0u64,
1115 handled_events_count: 0u64,
1116 last_generated_timestamp_ns: 0i64,
1117 last_seen_timestamp_ns: 0i64,
1118 },
1119 touch_screen: {
1120 events_count: 0u64,
1121 handled_events_count: 0u64,
1122 last_generated_timestamp_ns: 0i64,
1123 last_seen_timestamp_ns: 0i64,
1124 },
1125 touchpad: {
1126 events_count: 0u64,
1127 handled_events_count: 0u64,
1128 last_generated_timestamp_ns: 0i64,
1129 last_seen_timestamp_ns: 0i64,
1130 },
1131 }
1132 });
1133 }
1134
1135 #[test_case([i64::MIN]; "min value")]
1136 #[test_case([-1]; "negative value")]
1137 #[test_case([0]; "zero")]
1138 #[test_case([1]; "positive value")]
1139 #[test_case([i64::MAX]; "max value")]
1140 #[test_case([1_000_000, 10_000_000, 100_000_000, 1000_000_000]; "multiple values")]
1141 #[fuchsia::test(allow_stalls = false)]
1142 async fn updates_latency_histogram(
1143 latencies_nsec: impl IntoIterator<Item = i64> + Clone + 'static,
1144 ) {
1145 let inspector = inspect::Inspector::default();
1146 let root = inspector.root();
1147 let test_node = root.create_child("test_node");
1148
1149 let mut seen_timestamps =
1150 latencies_nsec.clone().into_iter().map(zx::MonotonicInstant::from_nanos);
1151 let now = move || {
1152 seen_timestamps.next().expect("internal error: test has more events than latencies")
1153 };
1154 let handler = super::InspectHandler::new_internal(
1155 test_node,
1156 now,
1157 &hashset! {},
1158 false,
1159 );
1160 for _latency in latencies_nsec.clone() {
1161 handler
1162 .clone()
1163 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::ZERO))
1164 .await;
1165 }
1166
1167 let mut histogram_assertion = diagnostics_assertions::HistogramAssertion::exponential(
1168 super::LATENCY_HISTOGRAM_PROPERTIES,
1169 );
1170 histogram_assertion
1171 .insert_values(latencies_nsec.into_iter().map(|nsec| nsec / 1000 / 1000));
1172 assert_data_tree!(inspector, root: {
1173 test_node: contains {
1174 pipeline_latency_ms: histogram_assertion
1175 }
1176 })
1177 }
1178}