Skip to main content

input_pipeline/
inspect_handler.rs

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