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