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