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 pressed_buttons,
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 },
592 "004_touchpad_event": {
593 event_time: AnyProperty,
594 pressed_buttons: pressed_buttons_vec,
595 injector_contacts: {
596 "1": {
597 position_x_mm: 0.0f64,
598 position_y_mm: 0.0f64,
599 },
600 "2": {
601 position_x_mm: 10.0f64,
602 position_y_mm: 10.0f64,
603 },
604 },
605 },
606 "005_light_sensor_event": {
607 event_time: AnyProperty,
608 red: 1u64,
609 green: 2u64,
610 blue: 3u64,
611 clear: 14747u64,
612 },
613 "006_keyboard_event": {
614 event_time: AnyProperty,
615 },
616 }
617 });
618 }
619
620 #[fasync::run_singlethreaded(test)]
621 async fn verify_inspect_no_recent_events_log() {
622 let inspector = inspect::Inspector::default();
623 let root = inspector.root();
624 let test_node = root.create_child("test_node");
625 let supported_input_devices: HashSet<&InputDeviceType> = HashSet::from([
626 &input_device::InputDeviceType::Keyboard,
627 &input_device::InputDeviceType::ConsumerControls,
628 &input_device::InputDeviceType::LightSensor,
629 &input_device::InputDeviceType::Mouse,
630 &input_device::InputDeviceType::Touch,
631 ]);
632
633 let handler = super::InspectHandler::new_internal(
634 test_node,
635 fixed_now,
636 &supported_input_devices,
637 false,
638 );
639 assert_data_tree!(inspector, root: {
640 test_node: contains {
641 events_count: 0u64,
642 last_seen_timestamp_ns: 0i64,
643 last_generated_timestamp_ns: 0i64,
644 consumer_controls: {
645 events_count: 0u64,
646 handled_events_count: 0u64,
647 last_generated_timestamp_ns: 0i64,
648 last_seen_timestamp_ns: 0i64,
649 },
650 fake: {
651 events_count: 0u64,
652 handled_events_count: 0u64,
653 last_generated_timestamp_ns: 0i64,
654 last_seen_timestamp_ns: 0i64,
655 },
656 keyboard: {
657 events_count: 0u64,
658 handled_events_count: 0u64,
659 last_generated_timestamp_ns: 0i64,
660 last_seen_timestamp_ns: 0i64,
661 },
662 light_sensor: {
663 events_count: 0u64,
664 handled_events_count: 0u64,
665 last_generated_timestamp_ns: 0i64,
666 last_seen_timestamp_ns: 0i64,
667 },
668 mouse: {
669 events_count: 0u64,
670 handled_events_count: 0u64,
671 last_generated_timestamp_ns: 0i64,
672 last_seen_timestamp_ns: 0i64,
673 },
674 touch_screen: {
675 events_count: 0u64,
676 handled_events_count: 0u64,
677 last_generated_timestamp_ns: 0i64,
678 last_seen_timestamp_ns: 0i64,
679 },
680 touchpad: {
681 events_count: 0u64,
682 handled_events_count: 0u64,
683 last_generated_timestamp_ns: 0i64,
684 last_seen_timestamp_ns: 0i64,
685 },
686 }
687 });
688
689 handler
690 .clone()
691 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
692 .await;
693 assert_data_tree!(inspector, root: {
694 test_node: contains {
695 events_count: 1u64,
696 last_seen_timestamp_ns: 42i64,
697 last_generated_timestamp_ns: 43i64,
698 consumer_controls: {
699 events_count: 0u64,
700 handled_events_count: 0u64,
701 last_generated_timestamp_ns: 0i64,
702 last_seen_timestamp_ns: 0i64,
703 },
704 fake: {
705 events_count: 1u64,
706 handled_events_count: 0u64,
707 last_generated_timestamp_ns: 43i64,
708 last_seen_timestamp_ns: 42i64,
709 },
710 keyboard: {
711 events_count: 0u64,
712 handled_events_count: 0u64,
713 last_generated_timestamp_ns: 0i64,
714 last_seen_timestamp_ns: 0i64,
715 },
716 light_sensor: {
717 events_count: 0u64,
718 handled_events_count: 0u64,
719 last_generated_timestamp_ns: 0i64,
720 last_seen_timestamp_ns: 0i64,
721 },
722 mouse: {
723 events_count: 0u64,
724 handled_events_count: 0u64,
725 last_generated_timestamp_ns: 0i64,
726 last_seen_timestamp_ns: 0i64,
727 },
728 touch_screen: {
729 events_count: 0u64,
730 handled_events_count: 0u64,
731 last_generated_timestamp_ns: 0i64,
732 last_seen_timestamp_ns: 0i64,
733 },
734 touchpad: {
735 events_count: 0u64,
736 handled_events_count: 0u64,
737 last_generated_timestamp_ns: 0i64,
738 last_seen_timestamp_ns: 0i64,
739 },
740 }
741 });
742
743 handler
744 .clone()
745 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
746 .await;
747 assert_data_tree!(inspector, root: {
748 test_node: contains {
749 events_count: 2u64,
750 last_seen_timestamp_ns: 42i64,
751 last_generated_timestamp_ns: 44i64,
752 consumer_controls: {
753 events_count: 0u64,
754 handled_events_count: 0u64,
755 last_generated_timestamp_ns: 0i64,
756 last_seen_timestamp_ns: 0i64,
757 },
758 fake: {
759 events_count: 2u64,
760 handled_events_count: 0u64,
761 last_generated_timestamp_ns: 44i64,
762 last_seen_timestamp_ns: 42i64,
763 },
764 keyboard: {
765 events_count: 0u64,
766 handled_events_count: 0u64,
767 last_generated_timestamp_ns: 0i64,
768 last_seen_timestamp_ns: 0i64,
769 },
770 light_sensor: {
771 events_count: 0u64,
772 handled_events_count: 0u64,
773 last_generated_timestamp_ns: 0i64,
774 last_seen_timestamp_ns: 0i64,
775 },
776 mouse: {
777 events_count: 0u64,
778 handled_events_count: 0u64,
779 last_generated_timestamp_ns: 0i64,
780 last_seen_timestamp_ns: 0i64,
781 },
782 touch_screen: {
783 events_count: 0u64,
784 handled_events_count: 0u64,
785 last_generated_timestamp_ns: 0i64,
786 last_seen_timestamp_ns: 0i64,
787 },
788 touchpad: {
789 events_count: 0u64,
790 handled_events_count: 0u64,
791 last_generated_timestamp_ns: 0i64,
792 last_seen_timestamp_ns: 0i64,
793 },
794 }
795 });
796
797 handler
798 .clone()
799 .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
800 44,
801 )))
802 .await;
803 assert_data_tree!(inspector, root: {
804 test_node: contains {
805 events_count: 3u64,
806 last_seen_timestamp_ns: 42i64,
807 last_generated_timestamp_ns: 44i64,
808 consumer_controls: {
809 events_count: 0u64,
810 handled_events_count: 0u64,
811 last_generated_timestamp_ns: 0i64,
812 last_seen_timestamp_ns: 0i64,
813 },
814 fake: {
815 events_count: 3u64,
816 handled_events_count: 1u64,
817 last_generated_timestamp_ns: 44i64,
818 last_seen_timestamp_ns: 42i64,
819 },
820 keyboard: {
821 events_count: 0u64,
822 handled_events_count: 0u64,
823 last_generated_timestamp_ns: 0i64,
824 last_seen_timestamp_ns: 0i64,
825 },
826 light_sensor: {
827 events_count: 0u64,
828 handled_events_count: 0u64,
829 last_generated_timestamp_ns: 0i64,
830 last_seen_timestamp_ns: 0i64,
831 },
832 mouse: {
833 events_count: 0u64,
834 handled_events_count: 0u64,
835 last_generated_timestamp_ns: 0i64,
836 last_seen_timestamp_ns: 0i64,
837 },
838 touch_screen: {
839 events_count: 0u64,
840 handled_events_count: 0u64,
841 last_generated_timestamp_ns: 0i64,
842 last_seen_timestamp_ns: 0i64,
843 },
844 touchpad: {
845 events_count: 0u64,
846 handled_events_count: 0u64,
847 last_generated_timestamp_ns: 0i64,
848 last_seen_timestamp_ns: 0i64,
849 },
850 }
851 });
852 }
853
854 #[fasync::run_singlethreaded(test)]
855 async fn verify_inspect_with_recent_events_log() {
856 let inspector = inspect::Inspector::default();
857 let root = inspector.root();
858 let test_node = root.create_child("test_node");
859 let supported_input_devices: HashSet<&InputDeviceType> = HashSet::from([
860 &input_device::InputDeviceType::Keyboard,
861 &input_device::InputDeviceType::ConsumerControls,
862 &input_device::InputDeviceType::LightSensor,
863 &input_device::InputDeviceType::Mouse,
864 &input_device::InputDeviceType::Touch,
865 ]);
866
867 let handler = super::InspectHandler::new_internal(
868 test_node,
869 fixed_now,
870 &supported_input_devices,
871 true,
872 );
873 assert_data_tree!(inspector, root: {
874 test_node: contains {
875 events_count: 0u64,
876 last_seen_timestamp_ns: 0i64,
877 last_generated_timestamp_ns: 0i64,
878 recent_events_log: {},
879 consumer_controls: {
880 events_count: 0u64,
881 handled_events_count: 0u64,
882 last_generated_timestamp_ns: 0i64,
883 last_seen_timestamp_ns: 0i64,
884 },
885 fake: {
886 events_count: 0u64,
887 handled_events_count: 0u64,
888 last_generated_timestamp_ns: 0i64,
889 last_seen_timestamp_ns: 0i64,
890 },
891 keyboard: {
892 events_count: 0u64,
893 handled_events_count: 0u64,
894 last_generated_timestamp_ns: 0i64,
895 last_seen_timestamp_ns: 0i64,
896 },
897 light_sensor: {
898 events_count: 0u64,
899 handled_events_count: 0u64,
900 last_generated_timestamp_ns: 0i64,
901 last_seen_timestamp_ns: 0i64,
902 },
903 mouse: {
904 events_count: 0u64,
905 handled_events_count: 0u64,
906 last_generated_timestamp_ns: 0i64,
907 last_seen_timestamp_ns: 0i64,
908 },
909 touch_screen: {
910 events_count: 0u64,
911 handled_events_count: 0u64,
912 last_generated_timestamp_ns: 0i64,
913 last_seen_timestamp_ns: 0i64,
914 },
915 touchpad: {
916 events_count: 0u64,
917 handled_events_count: 0u64,
918 last_generated_timestamp_ns: 0i64,
919 last_seen_timestamp_ns: 0i64,
920 },
921 }
922 });
923
924 handler
925 .clone()
926 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
927 .await;
928 assert_data_tree!(inspector, root: {
929 test_node: contains {
930 events_count: 1u64,
931 last_seen_timestamp_ns: 42i64,
932 last_generated_timestamp_ns: 43i64,
933 recent_events_log: {
934 "000_fake_event": {
935 event_time: 43i64,
936 },
937 },
938 consumer_controls: {
939 events_count: 0u64,
940 handled_events_count: 0u64,
941 last_generated_timestamp_ns: 0i64,
942 last_seen_timestamp_ns: 0i64,
943 },
944 fake: {
945 events_count: 1u64,
946 handled_events_count: 0u64,
947 last_generated_timestamp_ns: 43i64,
948 last_seen_timestamp_ns: 42i64,
949 },
950 keyboard: {
951 events_count: 0u64,
952 handled_events_count: 0u64,
953 last_generated_timestamp_ns: 0i64,
954 last_seen_timestamp_ns: 0i64,
955 },
956 light_sensor: {
957 events_count: 0u64,
958 handled_events_count: 0u64,
959 last_generated_timestamp_ns: 0i64,
960 last_seen_timestamp_ns: 0i64,
961 },
962 mouse: {
963 events_count: 0u64,
964 handled_events_count: 0u64,
965 last_generated_timestamp_ns: 0i64,
966 last_seen_timestamp_ns: 0i64,
967 },
968 touch_screen: {
969 events_count: 0u64,
970 handled_events_count: 0u64,
971 last_generated_timestamp_ns: 0i64,
972 last_seen_timestamp_ns: 0i64,
973 },
974 touchpad: {
975 events_count: 0u64,
976 handled_events_count: 0u64,
977 last_generated_timestamp_ns: 0i64,
978 last_seen_timestamp_ns: 0i64,
979 },
980 }
981 });
982
983 handler
984 .clone()
985 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
986 .await;
987 assert_data_tree!(inspector, root: {
988 test_node: contains {
989 events_count: 2u64,
990 last_seen_timestamp_ns: 42i64,
991 last_generated_timestamp_ns: 44i64,
992 recent_events_log: {
993 "000_fake_event": {
994 event_time: 43i64,
995 },
996 "001_fake_event": {
997 event_time: 44i64,
998 },
999 },
1000 consumer_controls: {
1001 events_count: 0u64,
1002 handled_events_count: 0u64,
1003 last_generated_timestamp_ns: 0i64,
1004 last_seen_timestamp_ns: 0i64,
1005 },
1006 fake: {
1007 events_count: 2u64,
1008 handled_events_count: 0u64,
1009 last_generated_timestamp_ns: 44i64,
1010 last_seen_timestamp_ns: 42i64,
1011 },
1012 keyboard: {
1013 events_count: 0u64,
1014 handled_events_count: 0u64,
1015 last_generated_timestamp_ns: 0i64,
1016 last_seen_timestamp_ns: 0i64,
1017 },
1018 light_sensor: {
1019 events_count: 0u64,
1020 handled_events_count: 0u64,
1021 last_generated_timestamp_ns: 0i64,
1022 last_seen_timestamp_ns: 0i64,
1023 },
1024 mouse: {
1025 events_count: 0u64,
1026 handled_events_count: 0u64,
1027 last_generated_timestamp_ns: 0i64,
1028 last_seen_timestamp_ns: 0i64,
1029 },
1030 touch_screen: {
1031 events_count: 0u64,
1032 handled_events_count: 0u64,
1033 last_generated_timestamp_ns: 0i64,
1034 last_seen_timestamp_ns: 0i64,
1035 },
1036 touchpad: {
1037 events_count: 0u64,
1038 handled_events_count: 0u64,
1039 last_generated_timestamp_ns: 0i64,
1040 last_seen_timestamp_ns: 0i64,
1041 },
1042 }
1043 });
1044
1045 handler
1046 .clone()
1047 .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
1048 44,
1049 )))
1050 .await;
1051 assert_data_tree!(inspector, root: {
1052 test_node: contains {
1053 events_count: 3u64,
1054 last_seen_timestamp_ns: 42i64,
1055 last_generated_timestamp_ns: 44i64,
1056 recent_events_log: {
1057 "000_fake_event": {
1058 event_time: 43i64,
1059 },
1060 "001_fake_event": {
1061 event_time: 44i64,
1062 },
1063 "002_fake_event": {
1064 event_time: 44i64,
1065 },
1066 },
1067 consumer_controls: {
1068 events_count: 0u64,
1069 handled_events_count: 0u64,
1070 last_generated_timestamp_ns: 0i64,
1071 last_seen_timestamp_ns: 0i64,
1072 },
1073 fake: {
1074 events_count: 3u64,
1075 handled_events_count: 1u64,
1076 last_generated_timestamp_ns: 44i64,
1077 last_seen_timestamp_ns: 42i64,
1078 },
1079 keyboard: {
1080 events_count: 0u64,
1081 handled_events_count: 0u64,
1082 last_generated_timestamp_ns: 0i64,
1083 last_seen_timestamp_ns: 0i64,
1084 },
1085 light_sensor: {
1086 events_count: 0u64,
1087 handled_events_count: 0u64,
1088 last_generated_timestamp_ns: 0i64,
1089 last_seen_timestamp_ns: 0i64,
1090 },
1091 mouse: {
1092 events_count: 0u64,
1093 handled_events_count: 0u64,
1094 last_generated_timestamp_ns: 0i64,
1095 last_seen_timestamp_ns: 0i64,
1096 },
1097 touch_screen: {
1098 events_count: 0u64,
1099 handled_events_count: 0u64,
1100 last_generated_timestamp_ns: 0i64,
1101 last_seen_timestamp_ns: 0i64,
1102 },
1103 touchpad: {
1104 events_count: 0u64,
1105 handled_events_count: 0u64,
1106 last_generated_timestamp_ns: 0i64,
1107 last_seen_timestamp_ns: 0i64,
1108 },
1109 }
1110 });
1111 }
1112
1113 #[test_case([i64::MIN]; "min value")]
1114 #[test_case([-1]; "negative value")]
1115 #[test_case([0]; "zero")]
1116 #[test_case([1]; "positive value")]
1117 #[test_case([i64::MAX]; "max value")]
1118 #[test_case([1_000_000, 10_000_000, 100_000_000, 1000_000_000]; "multiple values")]
1119 #[fuchsia::test(allow_stalls = false)]
1120 async fn updates_latency_histogram(
1121 latencies_nsec: impl IntoIterator<Item = i64> + Clone + 'static,
1122 ) {
1123 let inspector = inspect::Inspector::default();
1124 let root = inspector.root();
1125 let test_node = root.create_child("test_node");
1126
1127 let mut seen_timestamps =
1128 latencies_nsec.clone().into_iter().map(zx::MonotonicInstant::from_nanos);
1129 let now = move || {
1130 seen_timestamps.next().expect("internal error: test has more events than latencies")
1131 };
1132 let handler = super::InspectHandler::new_internal(
1133 test_node,
1134 now,
1135 &hashset! {},
1136 false,
1137 );
1138 for _latency in latencies_nsec.clone() {
1139 handler
1140 .clone()
1141 .handle_input_event(create_fake_input_event(zx::MonotonicInstant::ZERO))
1142 .await;
1143 }
1144
1145 let mut histogram_assertion = diagnostics_assertions::HistogramAssertion::exponential(
1146 super::LATENCY_HISTOGRAM_PROPERTIES,
1147 );
1148 histogram_assertion
1149 .insert_values(latencies_nsec.into_iter().map(|nsec| nsec / 1000 / 1000));
1150 assert_data_tree!(inspector, root: {
1151 test_node: contains {
1152 pipeline_latency_ms: histogram_assertion
1153 }
1154 })
1155 }
1156}