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