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, RawWheelDelta,
372        WheelDelta,
373    };
374    use crate::testing_utilities::{
375        consumer_controls_device_descriptor, create_consumer_controls_event,
376        create_fake_handled_input_event, create_fake_input_event, create_keyboard_event,
377        create_mouse_event, create_touch_contact, create_touch_screen_event, create_touchpad_event,
378        next_client_old_stream,
379    };
380    use crate::touch_binding::{TouchScreenDeviceDescriptor, TouchpadDeviceDescriptor};
381    use crate::utils::Position;
382    use diagnostics_assertions::{AnyProperty, assert_data_tree};
383    use fidl_fuchsia_input_report::InputDeviceMarker;
384    use fuchsia_async as fasync;
385    use sorted_vec_map::SortedVecMap;
386    use test_case::test_case;
387
388    fn fixed_now() -> zx::MonotonicInstant {
389        zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_nanos(42)
390    }
391
392    #[fasync::run_singlethreaded(test)]
393    async fn circular_buffer_no_overflow() {
394        let mut circular_buffer = CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE);
395        assert_eq!(circular_buffer._size, MAX_RECENT_EVENT_LOG_SIZE);
396
397        let first_event_time = zx::MonotonicInstant::get();
398        circular_buffer.push(create_fake_input_event(first_event_time));
399        let second_event_time = zx::MonotonicInstant::get();
400        circular_buffer.push(create_fake_input_event(second_event_time));
401
402        // Fill up `events` VecDeque
403        for _i in 2..MAX_RECENT_EVENT_LOG_SIZE {
404            let curr_event_time = zx::MonotonicInstant::get();
405            circular_buffer.push(create_fake_input_event(curr_event_time));
406            match circular_buffer._events.back() {
407                Some(event) => assert_eq!(event.event_time, curr_event_time),
408                None => assert!(false),
409            }
410        }
411
412        // Verify first event at the front
413        match circular_buffer._events.front() {
414            Some(event) => assert_eq!(event.event_time, first_event_time),
415            None => assert!(false),
416        }
417
418        // CircularBuffer `events` should be full, pushing another event should remove the first event.
419        let last_event_time = zx::MonotonicInstant::get();
420        circular_buffer.push(create_fake_input_event(last_event_time));
421        match circular_buffer._events.front() {
422            Some(event) => assert_eq!(event.event_time, second_event_time),
423            None => assert!(false),
424        }
425        match circular_buffer._events.back() {
426            Some(event) => assert_eq!(event.event_time, last_event_time),
427            None => assert!(false),
428        }
429    }
430
431    #[fasync::run_singlethreaded(test)]
432    async fn recent_events_log_records_inspect() {
433        let inspector = fuchsia_inspect::Inspector::default();
434
435        let recent_events_log =
436            Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
437        record_lazy_recent_events(inspector.root(), Arc::clone(&recent_events_log));
438
439        let keyboard_descriptor = InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
440            keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
441            ..Default::default()
442        });
443        let mouse_descriptor = InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
444            device_id: 1u32,
445            absolute_x_range: None,
446            absolute_y_range: None,
447            wheel_v_range: None,
448            wheel_h_range: None,
449            buttons: None,
450            counts_per_mm: 12u32,
451        });
452        let touch_screen_descriptor =
453            InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
454                device_id: 1,
455                contacts: vec![],
456            });
457        let touchpad_descriptor = InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
458            device_id: 1,
459            contacts: vec![],
460        });
461
462        let pressed_buttons = SortedVecSet::from(vec![1u8, 21u8, 15u8]);
463        let mut pressed_buttons_vec: Vec<u64> = vec![];
464        pressed_buttons.iter().for_each(|button| {
465            pressed_buttons_vec.push(*button as u64);
466        });
467
468        let (light_sensor_proxy, _) = next_client_old_stream::<
469            InputDeviceMarker,
470            fidl_next_fuchsia_input_report::InputDevice,
471        >();
472
473        let recent_events = vec![
474            create_keyboard_event(
475                fidl_fuchsia_input::Key::A,
476                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
477                None,
478                &keyboard_descriptor,
479                None,
480            ),
481            create_consumer_controls_event(
482                vec![
483                    fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
484                    fidl_fuchsia_input_report::ConsumerControlButton::VolumeUp,
485                    fidl_fuchsia_input_report::ConsumerControlButton::Pause,
486                    fidl_fuchsia_input_report::ConsumerControlButton::VolumeDown,
487                    fidl_fuchsia_input_report::ConsumerControlButton::MicMute,
488                    fidl_fuchsia_input_report::ConsumerControlButton::CameraDisable,
489                    fidl_fuchsia_input_report::ConsumerControlButton::FactoryReset,
490                    fidl_fuchsia_input_report::ConsumerControlButton::Reboot,
491                ],
492                zx::MonotonicInstant::get(),
493                &consumer_controls_device_descriptor(),
494            ),
495            create_mouse_event(
496                MouseLocation::Absolute(Position { x: 7.0f32, y: 15.0f32 }),
497                Some(WheelDelta {
498                    raw_data: RawWheelDelta::Ticks(5i64),
499                    physical_pixel: Some(8.0f32),
500                }),
501                Some(WheelDelta {
502                    raw_data: RawWheelDelta::Millimeters(10.0f32),
503                    physical_pixel: Some(8.0f32),
504                }),
505                Some(PrecisionScroll::Yes),
506                MousePhase::Move,
507                SortedVecSet::from(vec![1u8]),
508                pressed_buttons.clone(),
509                zx::MonotonicInstant::get(),
510                &mouse_descriptor,
511            ),
512            create_touch_screen_event(
513                SortedVecMap::from_iter(vec![
514                    (
515                        fidl_fuchsia_ui_input::PointerEventPhase::Add,
516                        vec![create_touch_contact(1u32, Position { x: 10.0, y: 30.0 })],
517                    ),
518                    (
519                        fidl_fuchsia_ui_input::PointerEventPhase::Move,
520                        vec![create_touch_contact(1u32, Position { x: 11.0, y: 31.0 })],
521                    ),
522                ]),
523                zx::MonotonicInstant::get(),
524                &touch_screen_descriptor,
525            ),
526            create_touchpad_event(
527                vec![
528                    create_touch_contact(1u32, Position { x: 0.0, y: 0.0 }),
529                    create_touch_contact(2u32, Position { x: 10.0, y: 10.0 }),
530                ],
531                SortedVecSet::new(),
532                zx::MonotonicInstant::get(),
533                &touchpad_descriptor,
534            ),
535            InputEvent {
536                device_event: InputDeviceEvent::LightSensor(LightSensorEvent {
537                    device_proxy: light_sensor_proxy,
538                    rgbc: Rgbc { red: 1, green: 2, blue: 3, clear: 14747 },
539                }),
540                device_descriptor: InputDeviceDescriptor::LightSensor(
541                    LightSensorDeviceDescriptor {
542                        vendor_id: 1,
543                        product_id: 2,
544                        device_id: 3,
545                        sensor_layout: Rgbc { red: 1, green: 2, blue: 3, clear: 4 },
546                    },
547                ),
548                event_time: zx::MonotonicInstant::get(),
549                handled: input_device::Handled::No,
550                trace_id: None,
551            },
552            create_keyboard_event(
553                fidl_fuchsia_input::Key::B,
554                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
555                None,
556                &keyboard_descriptor,
557                None,
558            ),
559        ];
560
561        for event in recent_events.into_iter() {
562            recent_events_log.lock().await.push(event);
563        }
564
565        assert_data_tree!(inspector, root: {
566            recent_events_log: {
567                "000_keyboard_event": {
568                    event_time: AnyProperty,
569                },
570                "001_consumer_controls_event": {
571                    event_time: AnyProperty,
572                    pressed_buttons: vec!["volume_up", "volume_up", "pause", "volume_down", "mic_mute", "camera_disable", "factory_reset", "reboot"],
573                },
574                "002_mouse_event": {
575                    event_time: AnyProperty,
576                    location_absolute: { x: 7.0f64, y: 15.0f64},
577                    wheel_delta_v: {
578                        ticks: 5i64,
579                        physical_pixel: 8.0f64,
580                    },
581                    wheel_delta_h: {
582                        millimeters: 10.0f64,
583                        physical_pixel: 8.0f64,
584                    },
585                    is_precision_scroll: "yes",
586                    phase: "move",
587                    affected_buttons: vec![1u64],
588                    pressed_buttons: pressed_buttons_vec.clone(),
589                },
590                "003_touch_screen_event": {
591                    event_time: AnyProperty,
592                    injector_contacts: {
593                        add: {
594                            "1": {
595                                position_x_mm: 10.0f64,
596                                position_y_mm: 30.0f64,
597                            },
598                        },
599                        change: {
600                            "1": {
601                                position_x_mm: 11.0f64,
602                                position_y_mm: 31.0f64,
603                            },
604                        },
605                        remove: {},
606                    },
607                    pressed_buttons: Vec::<String>::new(),
608                },
609                "004_touchpad_event": {
610                    event_time: AnyProperty,
611                    pressed_buttons: Vec::<u64>::new(),
612                    injector_contacts: {
613                        "1": {
614                            position_x_mm: 0.0f64,
615                            position_y_mm: 0.0f64,
616                        },
617                        "2": {
618                            position_x_mm: 10.0f64,
619                            position_y_mm: 10.0f64,
620                        },
621                    },
622                },
623                "005_light_sensor_event": {
624                    event_time: AnyProperty,
625                    red: 1u64,
626                    green: 2u64,
627                    blue: 3u64,
628                    clear: 14747u64,
629                },
630                "006_keyboard_event": {
631                    event_time: AnyProperty,
632                },
633            }
634        });
635    }
636
637    #[fasync::run_singlethreaded(test)]
638    async fn verify_inspect_no_recent_events_log() {
639        let inspector = inspect::Inspector::default();
640        let root = inspector.root();
641        let test_node = root.create_child("test_node");
642        let supported_input_devices: SortedVecSet<&InputDeviceType> = SortedVecSet::from([
643            &input_device::InputDeviceType::Keyboard,
644            &input_device::InputDeviceType::ConsumerControls,
645            &input_device::InputDeviceType::LightSensor,
646            &input_device::InputDeviceType::Mouse,
647            &input_device::InputDeviceType::Touch,
648        ]);
649
650        let handler = super::InspectHandler::new_internal(
651            test_node,
652            fixed_now,
653            &supported_input_devices,
654            /* displays_recent_events = */ false,
655        );
656        assert_data_tree!(inspector, root: {
657            test_node: contains {
658                events_count: 0u64,
659                last_seen_timestamp_ns: 0i64,
660                last_generated_timestamp_ns: 0i64,
661                consumer_controls: {
662                     events_count: 0u64,
663                     events_with_wake_lease_count: 0u64,
664                     handled_events_count: 0u64,
665                     last_generated_timestamp_ns: 0i64,
666                     last_seen_timestamp_ns: 0i64,
667                },
668                fake: {
669                     events_count: 0u64,
670                     events_with_wake_lease_count: 0u64,
671                     handled_events_count: 0u64,
672                     last_generated_timestamp_ns: 0i64,
673                     last_seen_timestamp_ns: 0i64,
674                },
675                keyboard: {
676                     events_count: 0u64,
677                     events_with_wake_lease_count: 0u64,
678                     handled_events_count: 0u64,
679                     last_generated_timestamp_ns: 0i64,
680                     last_seen_timestamp_ns: 0i64,
681                },
682                light_sensor: {
683                     events_count: 0u64,
684                     events_with_wake_lease_count: 0u64,
685                     handled_events_count: 0u64,
686                     last_generated_timestamp_ns: 0i64,
687                     last_seen_timestamp_ns: 0i64,
688                },
689                mouse: {
690                     events_count: 0u64,
691                     events_with_wake_lease_count: 0u64,
692                     handled_events_count: 0u64,
693                     last_generated_timestamp_ns: 0i64,
694                     last_seen_timestamp_ns: 0i64,
695                },
696                touch_screen: {
697                     events_count: 0u64,
698                     events_with_wake_lease_count: 0u64,
699                     handled_events_count: 0u64,
700                     last_generated_timestamp_ns: 0i64,
701                     last_seen_timestamp_ns: 0i64,
702                },
703                touchpad: {
704                    events_count: 0u64,
705                    events_with_wake_lease_count: 0u64,
706                    handled_events_count: 0u64,
707                    last_generated_timestamp_ns: 0i64,
708                    last_seen_timestamp_ns: 0i64,
709               },
710           }
711        });
712
713        handler
714            .clone()
715            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
716            .await;
717        assert_data_tree!(inspector, root: {
718            test_node: contains {
719                events_count: 1u64,
720                last_seen_timestamp_ns: 42i64,
721                last_generated_timestamp_ns: 43i64,
722                consumer_controls: {
723                     events_count: 0u64,
724                     events_with_wake_lease_count: 0u64,
725                     handled_events_count: 0u64,
726                     last_generated_timestamp_ns: 0i64,
727                     last_seen_timestamp_ns: 0i64,
728                },
729                fake: {
730                     events_count: 1u64,
731                     events_with_wake_lease_count: 0u64, // Fake event doesn't have wake lease in this test setup
732                     handled_events_count: 0u64,
733                     last_generated_timestamp_ns: 43i64,
734                     last_seen_timestamp_ns: 42i64,
735                },
736                keyboard: {
737                     events_count: 0u64,
738                     events_with_wake_lease_count: 0u64,
739                     handled_events_count: 0u64,
740                     last_generated_timestamp_ns: 0i64,
741                     last_seen_timestamp_ns: 0i64,
742                },
743                light_sensor: {
744                     events_count: 0u64,
745                     events_with_wake_lease_count: 0u64,
746                     handled_events_count: 0u64,
747                     last_generated_timestamp_ns: 0i64,
748                     last_seen_timestamp_ns: 0i64,
749                },
750                mouse: {
751                     events_count: 0u64,
752                     events_with_wake_lease_count: 0u64,
753                     handled_events_count: 0u64,
754                     last_generated_timestamp_ns: 0i64,
755                     last_seen_timestamp_ns: 0i64,
756                },
757                touch_screen: {
758                     events_count: 0u64,
759                     events_with_wake_lease_count: 0u64,
760                     handled_events_count: 0u64,
761                     last_generated_timestamp_ns: 0i64,
762                     last_seen_timestamp_ns: 0i64,
763                },
764                touchpad: {
765                    events_count: 0u64,
766                    events_with_wake_lease_count: 0u64,
767                    handled_events_count: 0u64,
768                    last_generated_timestamp_ns: 0i64,
769                    last_seen_timestamp_ns: 0i64,
770               },
771            }
772        });
773
774        handler
775            .clone()
776            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
777            .await;
778        assert_data_tree!(inspector, root: {
779            test_node: contains {
780                events_count: 2u64,
781                last_seen_timestamp_ns: 42i64,
782                last_generated_timestamp_ns: 44i64,
783                consumer_controls: {
784                     events_count: 0u64,
785                     events_with_wake_lease_count: 0u64,
786                     handled_events_count: 0u64,
787                     last_generated_timestamp_ns: 0i64,
788                     last_seen_timestamp_ns: 0i64,
789                },
790                fake: {
791                     events_count: 2u64,
792                     events_with_wake_lease_count: 0u64,
793                     handled_events_count: 0u64,
794                     last_generated_timestamp_ns: 44i64,
795                     last_seen_timestamp_ns: 42i64,
796                },
797                keyboard: {
798                     events_count: 0u64,
799                     events_with_wake_lease_count: 0u64,
800                     handled_events_count: 0u64,
801                     last_generated_timestamp_ns: 0i64,
802                     last_seen_timestamp_ns: 0i64,
803                },
804                light_sensor: {
805                     events_count: 0u64,
806                     events_with_wake_lease_count: 0u64,
807                     handled_events_count: 0u64,
808                     last_generated_timestamp_ns: 0i64,
809                     last_seen_timestamp_ns: 0i64,
810                },
811                mouse: {
812                     events_count: 0u64,
813                     events_with_wake_lease_count: 0u64,
814                     handled_events_count: 0u64,
815                     last_generated_timestamp_ns: 0i64,
816                     last_seen_timestamp_ns: 0i64,
817                },
818                touch_screen: {
819                     events_count: 0u64,
820                     events_with_wake_lease_count: 0u64,
821                     handled_events_count: 0u64,
822                     last_generated_timestamp_ns: 0i64,
823                     last_seen_timestamp_ns: 0i64,
824                },
825                touchpad: {
826                    events_count: 0u64,
827                    events_with_wake_lease_count: 0u64,
828                    handled_events_count: 0u64,
829                    last_generated_timestamp_ns: 0i64,
830                    last_seen_timestamp_ns: 0i64,
831               },
832            }
833        });
834
835        handler
836            .clone()
837            .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
838                44,
839            )))
840            .await;
841        assert_data_tree!(inspector, root: {
842            test_node: contains {
843                events_count: 3u64,
844                last_seen_timestamp_ns: 42i64,
845                last_generated_timestamp_ns: 44i64,
846                consumer_controls: {
847                     events_count: 0u64,
848                     events_with_wake_lease_count: 0u64,
849                     handled_events_count: 0u64,
850                     last_generated_timestamp_ns: 0i64,
851                     last_seen_timestamp_ns: 0i64,
852                },
853                fake: {
854                     events_count: 3u64,
855                     events_with_wake_lease_count: 0u64,
856                     handled_events_count: 1u64,
857                     last_generated_timestamp_ns: 44i64,
858                     last_seen_timestamp_ns: 42i64,
859                },
860                keyboard: {
861                     events_count: 0u64,
862                     events_with_wake_lease_count: 0u64,
863                     handled_events_count: 0u64,
864                     last_generated_timestamp_ns: 0i64,
865                     last_seen_timestamp_ns: 0i64,
866                },
867                light_sensor: {
868                     events_count: 0u64,
869                     events_with_wake_lease_count: 0u64,
870                     handled_events_count: 0u64,
871                     last_generated_timestamp_ns: 0i64,
872                     last_seen_timestamp_ns: 0i64,
873                },
874                mouse: {
875                     events_count: 0u64,
876                     events_with_wake_lease_count: 0u64,
877                     handled_events_count: 0u64,
878                     last_generated_timestamp_ns: 0i64,
879                     last_seen_timestamp_ns: 0i64,
880                },
881                touch_screen: {
882                     events_count: 0u64,
883                     events_with_wake_lease_count: 0u64,
884                     handled_events_count: 0u64,
885                     last_generated_timestamp_ns: 0i64,
886                     last_seen_timestamp_ns: 0i64,
887                },
888                touchpad: {
889                    events_count: 0u64,
890                    events_with_wake_lease_count: 0u64,
891                    handled_events_count: 0u64,
892                    last_generated_timestamp_ns: 0i64,
893                    last_seen_timestamp_ns: 0i64,
894               },
895            }
896        });
897    }
898
899    #[fasync::run_singlethreaded(test)]
900    async fn verify_inspect_with_recent_events_log() {
901        let inspector = inspect::Inspector::default();
902        let root = inspector.root();
903        let test_node = root.create_child("test_node");
904        let supported_input_devices: SortedVecSet<&InputDeviceType> = SortedVecSet::from([
905            &input_device::InputDeviceType::Keyboard,
906            &input_device::InputDeviceType::ConsumerControls,
907            &input_device::InputDeviceType::LightSensor,
908            &input_device::InputDeviceType::Mouse,
909            &input_device::InputDeviceType::Touch,
910        ]);
911
912        let handler = super::InspectHandler::new_internal(
913            test_node,
914            fixed_now,
915            &supported_input_devices,
916            /* displays_recent_events = */ true,
917        );
918        assert_data_tree!(inspector, root: {
919            test_node: contains {
920                events_count: 0u64,
921                last_seen_timestamp_ns: 0i64,
922                last_generated_timestamp_ns: 0i64,
923                recent_events_log: {},
924                consumer_controls: {
925                     events_count: 0u64,
926                     events_with_wake_lease_count: 0u64,
927                     handled_events_count: 0u64,
928                     last_generated_timestamp_ns: 0i64,
929                     last_seen_timestamp_ns: 0i64,
930                },
931                fake: {
932                     events_count: 0u64,
933                     events_with_wake_lease_count: 0u64,
934                     handled_events_count: 0u64,
935                     last_generated_timestamp_ns: 0i64,
936                     last_seen_timestamp_ns: 0i64,
937                },
938                keyboard: {
939                     events_count: 0u64,
940                     events_with_wake_lease_count: 0u64,
941                     handled_events_count: 0u64,
942                     last_generated_timestamp_ns: 0i64,
943                     last_seen_timestamp_ns: 0i64,
944                },
945                light_sensor: {
946                     events_count: 0u64,
947                     events_with_wake_lease_count: 0u64,
948                     handled_events_count: 0u64,
949                     last_generated_timestamp_ns: 0i64,
950                     last_seen_timestamp_ns: 0i64,
951                },
952                mouse: {
953                     events_count: 0u64,
954                     events_with_wake_lease_count: 0u64,
955                     handled_events_count: 0u64,
956                     last_generated_timestamp_ns: 0i64,
957                     last_seen_timestamp_ns: 0i64,
958                },
959                touch_screen: {
960                     events_count: 0u64,
961                     events_with_wake_lease_count: 0u64,
962                     handled_events_count: 0u64,
963                     last_generated_timestamp_ns: 0i64,
964                     last_seen_timestamp_ns: 0i64,
965                },
966                touchpad: {
967                    events_count: 0u64,
968                    events_with_wake_lease_count: 0u64,
969                    handled_events_count: 0u64,
970                    last_generated_timestamp_ns: 0i64,
971                    last_seen_timestamp_ns: 0i64,
972               },
973           }
974        });
975
976        handler
977            .clone()
978            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
979            .await;
980        assert_data_tree!(inspector, root: {
981            test_node: contains {
982                events_count: 1u64,
983                last_seen_timestamp_ns: 42i64,
984                last_generated_timestamp_ns: 43i64,
985                recent_events_log: {
986                    "000_fake_event": {
987                        event_time: 43i64,
988                    },
989                },
990                consumer_controls: {
991                     events_count: 0u64,
992                     events_with_wake_lease_count: 0u64,
993                     handled_events_count: 0u64,
994                     last_generated_timestamp_ns: 0i64,
995                     last_seen_timestamp_ns: 0i64,
996                },
997                fake: {
998                     events_count: 1u64,
999                     events_with_wake_lease_count: 0u64,
1000                     handled_events_count: 0u64,
1001                     last_generated_timestamp_ns: 43i64,
1002                     last_seen_timestamp_ns: 42i64,
1003                },
1004                keyboard: {
1005                     events_count: 0u64,
1006                     events_with_wake_lease_count: 0u64,
1007                     handled_events_count: 0u64,
1008                     last_generated_timestamp_ns: 0i64,
1009                     last_seen_timestamp_ns: 0i64,
1010                },
1011                light_sensor: {
1012                     events_count: 0u64,
1013                     events_with_wake_lease_count: 0u64,
1014                     handled_events_count: 0u64,
1015                     last_generated_timestamp_ns: 0i64,
1016                     last_seen_timestamp_ns: 0i64,
1017                },
1018                mouse: {
1019                     events_count: 0u64,
1020                     events_with_wake_lease_count: 0u64,
1021                     handled_events_count: 0u64,
1022                     last_generated_timestamp_ns: 0i64,
1023                     last_seen_timestamp_ns: 0i64,
1024                },
1025                touch_screen: {
1026                     events_count: 0u64,
1027                     events_with_wake_lease_count: 0u64,
1028                     handled_events_count: 0u64,
1029                     last_generated_timestamp_ns: 0i64,
1030                     last_seen_timestamp_ns: 0i64,
1031                },
1032                touchpad: {
1033                    events_count: 0u64,
1034                    events_with_wake_lease_count: 0u64,
1035                    handled_events_count: 0u64,
1036                    last_generated_timestamp_ns: 0i64,
1037                    last_seen_timestamp_ns: 0i64,
1038               },
1039            }
1040        });
1041
1042        handler
1043            .clone()
1044            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
1045            .await;
1046        assert_data_tree!(inspector, root: {
1047            test_node: contains {
1048                events_count: 2u64,
1049                last_seen_timestamp_ns: 42i64,
1050                last_generated_timestamp_ns: 44i64,
1051                recent_events_log: {
1052                    "000_fake_event": {
1053                        event_time: 43i64,
1054                    },
1055                    "001_fake_event": {
1056                        event_time: 44i64,
1057                    },
1058                },
1059                consumer_controls: {
1060                     events_count: 0u64,
1061                     events_with_wake_lease_count: 0u64,
1062                     handled_events_count: 0u64,
1063                     last_generated_timestamp_ns: 0i64,
1064                     last_seen_timestamp_ns: 0i64,
1065                },
1066                fake: {
1067                     events_count: 2u64,
1068                     events_with_wake_lease_count: 0u64,
1069                     handled_events_count: 0u64,
1070                     last_generated_timestamp_ns: 44i64,
1071                     last_seen_timestamp_ns: 42i64,
1072                },
1073                keyboard: {
1074                     events_count: 0u64,
1075                     events_with_wake_lease_count: 0u64,
1076                     handled_events_count: 0u64,
1077                     last_generated_timestamp_ns: 0i64,
1078                     last_seen_timestamp_ns: 0i64,
1079                },
1080                light_sensor: {
1081                     events_count: 0u64,
1082                     events_with_wake_lease_count: 0u64,
1083                     handled_events_count: 0u64,
1084                     last_generated_timestamp_ns: 0i64,
1085                     last_seen_timestamp_ns: 0i64,
1086                },
1087                mouse: {
1088                     events_count: 0u64,
1089                     events_with_wake_lease_count: 0u64,
1090                     handled_events_count: 0u64,
1091                     last_generated_timestamp_ns: 0i64,
1092                     last_seen_timestamp_ns: 0i64,
1093                },
1094                touch_screen: {
1095                     events_count: 0u64,
1096                     events_with_wake_lease_count: 0u64,
1097                     handled_events_count: 0u64,
1098                     last_generated_timestamp_ns: 0i64,
1099                     last_seen_timestamp_ns: 0i64,
1100                },
1101                touchpad: {
1102                    events_count: 0u64,
1103                    events_with_wake_lease_count: 0u64,
1104                    handled_events_count: 0u64,
1105                    last_generated_timestamp_ns: 0i64,
1106                    last_seen_timestamp_ns: 0i64,
1107               },
1108            }
1109        });
1110
1111        handler
1112            .clone()
1113            .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
1114                44,
1115            )))
1116            .await;
1117        assert_data_tree!(inspector, root: {
1118            test_node: contains {
1119                events_count: 3u64,
1120                last_seen_timestamp_ns: 42i64,
1121                last_generated_timestamp_ns: 44i64,
1122                recent_events_log: {
1123                    "000_fake_event": {
1124                        event_time: 43i64,
1125                    },
1126                    "001_fake_event": {
1127                        event_time: 44i64,
1128                    },
1129                    "002_fake_event": {
1130                        event_time: 44i64,
1131                    },
1132                },
1133                consumer_controls: {
1134                     events_count: 0u64,
1135                     events_with_wake_lease_count: 0u64,
1136                     handled_events_count: 0u64,
1137                     last_generated_timestamp_ns: 0i64,
1138                     last_seen_timestamp_ns: 0i64,
1139                },
1140                fake: {
1141                     events_count: 3u64,
1142                     events_with_wake_lease_count: 0u64,
1143                     handled_events_count: 1u64,
1144                     last_generated_timestamp_ns: 44i64,
1145                     last_seen_timestamp_ns: 42i64,
1146                },
1147                keyboard: {
1148                     events_count: 0u64,
1149                     events_with_wake_lease_count: 0u64,
1150                     handled_events_count: 0u64,
1151                     last_generated_timestamp_ns: 0i64,
1152                     last_seen_timestamp_ns: 0i64,
1153                },
1154                light_sensor: {
1155                     events_count: 0u64,
1156                     events_with_wake_lease_count: 0u64,
1157                     handled_events_count: 0u64,
1158                     last_generated_timestamp_ns: 0i64,
1159                     last_seen_timestamp_ns: 0i64,
1160                },
1161                mouse: {
1162                     events_count: 0u64,
1163                     events_with_wake_lease_count: 0u64,
1164                     handled_events_count: 0u64,
1165                     last_generated_timestamp_ns: 0i64,
1166                     last_seen_timestamp_ns: 0i64,
1167                },
1168                touch_screen: {
1169                     events_count: 0u64,
1170                     events_with_wake_lease_count: 0u64,
1171                     handled_events_count: 0u64,
1172                     last_generated_timestamp_ns: 0i64,
1173                     last_seen_timestamp_ns: 0i64,
1174                },
1175                touchpad: {
1176                    events_count: 0u64,
1177                    events_with_wake_lease_count: 0u64,
1178                    handled_events_count: 0u64,
1179                    last_generated_timestamp_ns: 0i64,
1180                    last_seen_timestamp_ns: 0i64,
1181               },
1182            }
1183        });
1184    }
1185
1186    #[test_case([i64::MIN]; "min value")]
1187    #[test_case([-1]; "negative value")]
1188    #[test_case([0]; "zero")]
1189    #[test_case([1]; "positive value")]
1190    #[test_case([i64::MAX]; "max value")]
1191    #[test_case([1_000_000, 10_000_000, 100_000_000, 1000_000_000]; "multiple values")]
1192    #[fuchsia::test(allow_stalls = false)]
1193    async fn updates_latency_histogram(
1194        latencies_nsec: impl IntoIterator<Item = i64> + Clone + 'static,
1195    ) {
1196        let inspector = inspect::Inspector::default();
1197        let root = inspector.root();
1198        let test_node = root.create_child("test_node");
1199
1200        let mut seen_timestamps =
1201            latencies_nsec.clone().into_iter().map(zx::MonotonicInstant::from_nanos);
1202        let now = move || {
1203            seen_timestamps.next().expect("internal error: test has more events than latencies")
1204        };
1205        let handler = super::InspectHandler::new_internal(
1206            test_node,
1207            now,
1208            &SortedVecSet::new(),
1209            /* displays_recent_events = */ false,
1210        );
1211        for _latency in latencies_nsec.clone() {
1212            handler
1213                .clone()
1214                .handle_input_event(create_fake_input_event(zx::MonotonicInstant::ZERO))
1215                .await;
1216        }
1217
1218        let mut histogram_assertion = diagnostics_assertions::HistogramAssertion::exponential(
1219            super::LATENCY_HISTOGRAM_PROPERTIES,
1220        );
1221        histogram_assertion
1222            .insert_values(latencies_nsec.into_iter().map(|nsec| nsec / 1000 / 1000));
1223        assert_data_tree!(inspector, root: {
1224            test_node: contains {
1225                pipeline_latency_ms: histogram_assertion
1226            }
1227        })
1228    }
1229}