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