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