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