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