input_pipeline/
ime_handler.rs

1// Copyright 2020 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_handler::{Handler, InputHandlerStatus, UnhandledInputHandler};
6use crate::{input_device, keyboard_binding, metrics};
7use anyhow::Error;
8use async_trait::async_trait;
9use fidl_fuchsia_ui_input3::{self as fidl_ui_input3, LockState, Modifiers};
10use fuchsia_component::client::connect_to_protocol;
11use fuchsia_inspect::health::Reporter;
12
13use keymaps::{LockStateChecker, ModifierChecker};
14use metrics_registry::*;
15use std::rc::Rc;
16
17#[derive(Debug)]
18pub struct FrozenLockState {
19    lock_state: LockState,
20}
21
22impl From<LockState> for FrozenLockState {
23    fn from(lock_state: LockState) -> Self {
24        FrozenLockState { lock_state }
25    }
26}
27
28impl LockStateChecker for FrozenLockState {
29    fn test(&self, value: LockState) -> bool {
30        self.lock_state.contains(value)
31    }
32}
33
34/// Modifier state plus a tester method.
35#[derive(Debug)]
36pub struct FrozenModifierState {
37    state: Modifiers,
38}
39
40impl From<fidl_fuchsia_ui_input3::Modifiers> for FrozenModifierState {
41    fn from(state: Modifiers) -> Self {
42        FrozenModifierState { state }
43    }
44}
45
46impl ModifierChecker for FrozenModifierState {
47    fn test(&self, value: Modifiers) -> bool {
48        self.state.contains(value)
49    }
50}
51
52/// [`ImeHandler`] is responsible for dispatching key events to the IME service, thus making sure
53/// that key events are delivered to application runtimes (e.g., web, Flutter).
54///
55/// > NOTE: The [ImeHandler] requires [ModifierHandler] to be installed upstream to apply the keymaps correctly.
56pub struct ImeHandler {
57    /// The FIDL proxy (client-side stub) to the service for key event injection.
58    key_event_injector: fidl_ui_input3::KeyEventInjectorProxy,
59
60    /// The inventory of this handler's Inspect status.
61    pub inspect_status: InputHandlerStatus,
62
63    /// The metrics logger.
64    metrics_logger: metrics::MetricsLogger,
65}
66
67impl Handler for ImeHandler {
68    fn set_handler_healthy(self: std::rc::Rc<Self>) {
69        self.inspect_status.health_node.borrow_mut().set_ok();
70    }
71
72    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
73        self.inspect_status.health_node.borrow_mut().set_unhealthy(msg);
74    }
75
76    fn get_name(&self) -> &'static str {
77        "ImeHandler"
78    }
79
80    fn interest(&self) -> Vec<input_device::InputEventType> {
81        vec![input_device::InputEventType::Keyboard]
82    }
83}
84
85#[async_trait(?Send)]
86impl UnhandledInputHandler for ImeHandler {
87    async fn handle_unhandled_input_event(
88        self: Rc<Self>,
89        unhandled_input_event: input_device::UnhandledInputEvent,
90    ) -> Vec<input_device::InputEvent> {
91        fuchsia_trace::duration!("input", "ime_handler");
92        match unhandled_input_event {
93            input_device::UnhandledInputEvent {
94                device_event: input_device::InputDeviceEvent::Keyboard(ref keyboard_device_event),
95                device_descriptor:
96                    input_device::InputDeviceDescriptor::Keyboard(ref keyboard_description),
97                event_time,
98                trace_id,
99            } => {
100                fuchsia_trace::duration!("input", "ime_handler[processing]");
101                if let Some(trace_id) = trace_id {
102                    fuchsia_trace::flow_end!("input", "event_in_input_pipeline", trace_id.into());
103                }
104
105                self.inspect_status.count_received_event(&event_time);
106                let key_event = create_key_event(
107                    &keyboard_device_event,
108                    event_time,
109                    keyboard_description.device_id,
110                );
111                self.dispatch_key(key_event).await;
112                // Consume the input event.
113                self.inspect_status.count_handled_event();
114                vec![input_device::InputEvent::from(unhandled_input_event).into_handled()]
115            }
116            _ => {
117                // TODO: b/478249522 - add cobalt logging
118                log::warn!("Unhandled input event: {:?}", unhandled_input_event.get_event_type());
119                vec![input_device::InputEvent::from(unhandled_input_event)]
120            }
121        }
122    }
123}
124
125impl ImeHandler {
126    /// Creates a new [`ImeHandler`] by connecting out to the key event injector.
127    pub async fn new(
128        input_handlers_node: &fuchsia_inspect::Node,
129        metrics_logger: metrics::MetricsLogger,
130    ) -> Result<Rc<Self>, Error> {
131        let key_event_injector = connect_to_protocol::<fidl_ui_input3::KeyEventInjectorMarker>()?;
132
133        Self::new_handler(key_event_injector, input_handlers_node, metrics_logger).await
134    }
135
136    /// Creates a new [`ImeHandler`].
137    ///
138    /// # Parameters
139    /// `key_event_injector`: A proxy (FIDL client-side stub) to the key event
140    /// injector FIDL service.
141    async fn new_handler(
142        key_event_injector: fidl_ui_input3::KeyEventInjectorProxy,
143        input_handlers_node: &fuchsia_inspect::Node,
144        metrics_logger: metrics::MetricsLogger,
145    ) -> Result<Rc<Self>, Error> {
146        let inspect_status = InputHandlerStatus::new(
147            input_handlers_node,
148            "ime_handler",
149            /* generates_events */ false,
150        );
151        let handler = ImeHandler { key_event_injector, inspect_status, metrics_logger };
152
153        Ok(Rc::new(handler))
154    }
155
156    /// Dispatches key events to IME and returns KeyboardEvents for unhandled events.
157    ///
158    /// # Parameters
159    /// `key_events`: The key events to dispatch.
160    /// `event_time`: The time in nanoseconds when the events were first recorded.
161    async fn dispatch_key(self: &Rc<Self>, key_event: fidl_ui_input3::KeyEvent) {
162        assert!(
163            key_event.timestamp.is_some(),
164            "dispatch_key: got a key_event without a timestamp: {:?}",
165            &key_event
166        );
167        match self.key_event_injector.inject(&key_event).await {
168            Err(err) => self.metrics_logger.log_error(
169                InputPipelineErrorMetricDimensionEvent::ImeFailedToDispatchKeyToIme,
170                std::format!("Failed to dispatch key to IME: {:?}", err),
171            ),
172            _ => {}
173        };
174    }
175}
176
177/// Returns a KeyEvent with the given parameters.
178///
179/// # Parameters
180/// * `event`: The keyboard event to process.
181/// * `event_time`: The time in nanoseconds when the event was first recorded.
182fn create_key_event(
183    event: &keyboard_binding::KeyboardEvent,
184    event_time: zx::MonotonicInstant,
185    device_id: u32,
186) -> fidl_ui_input3::KeyEvent {
187    let modifier_state: FrozenModifierState =
188        event.get_modifiers().unwrap_or_else(|| Modifiers::from_bits_allow_unknown(0)).into();
189    let lock_state: FrozenLockState =
190        event.get_lock_state().unwrap_or_else(|| LockState::from_bits_allow_unknown(0)).into();
191    log::debug!(
192        "ImeHandler::create_key_event: key:{:?}, modifier_state: {:?}, lock_state: {:?}, event_type: {:?}",
193        event.get_key(),
194        modifier_state,
195        lock_state,
196        event.get_event_type(),
197    );
198    // Don't override the key meaning if already set, e.g. by prior stage.
199    let key_meaning = event
200        .get_key_meaning()
201        .or_else(|| keymaps::US_QWERTY.apply(event.get_key(), &modifier_state, &lock_state));
202
203    // Don't insert a spurious Some(0).
204    let repeat_sequence = match event.get_repeat_sequence() {
205        0 => None,
206        s => Some(s),
207    };
208
209    fidl_ui_input3::KeyEvent {
210        timestamp: Some(event_time.into_nanos()),
211        type_: event.get_event_type().into(),
212        key: event.get_key().into(),
213        modifiers: event.get_modifiers(),
214        lock_state: event.get_lock_state(),
215        key_meaning,
216        repeat_sequence,
217        device_id: Some(device_id),
218        ..Default::default()
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use crate::input_handler::InputHandler;
226    use crate::keyboard_binding::{self, KeyboardEvent};
227    use crate::testing_utilities;
228    use assert_matches::assert_matches;
229    use futures::StreamExt;
230    use std::convert::TryFrom as _;
231    use test_case::test_case;
232    use {
233        fidl_fuchsia_input as fidl_input, fidl_fuchsia_ui_input3 as fidl_ui_input3,
234        fuchsia_async as fasync,
235    };
236
237    fn handle_events(
238        ime_handler: Rc<ImeHandler>,
239        input_events: Vec<input_device::UnhandledInputEvent>,
240    ) {
241        fasync::Task::local(async move {
242            for input_event in input_events {
243                assert_matches!(
244                    ime_handler.clone().handle_unhandled_input_event(input_event).await.as_slice(),
245                    [input_device::InputEvent { handled: input_device::Handled::Yes, .. }]
246                );
247            }
248        })
249        .detach();
250    }
251
252    async fn assert_ime_receives_events(
253        expected_events: Vec<fidl_ui_input3::KeyEvent>,
254        mut request_stream: fidl_ui_input3::KeyEventInjectorRequestStream,
255    ) {
256        let mut expected_events_iter = expected_events.iter().peekable();
257        while let Some(Ok(fidl_ui_input3::KeyEventInjectorRequest::Inject {
258            key_event,
259            responder,
260            ..
261        })) = request_stream.next().await
262        {
263            pretty_assertions::assert_eq!(&key_event, expected_events_iter.next().unwrap());
264
265            // All the expected events have been received, so make sure no more events
266            // are present before returning.
267            if expected_events_iter.peek().is_none() {
268                responder
269                    .send(fidl_ui_input3::KeyEventStatus::Handled)
270                    .expect("error responding to DispatchKey");
271                return;
272            }
273            responder
274                .send(fidl_ui_input3::KeyEventStatus::Handled)
275                .expect("error responding to DispatchKey");
276        }
277
278        assert!(false);
279    }
280
281    fn connect_to_key_event_injector()
282    -> (fidl_ui_input3::KeyEventInjectorProxy, fidl_ui_input3::KeyEventInjectorRequestStream) {
283        fidl::endpoints::create_proxy_and_stream::<fidl_ui_input3::KeyEventInjectorMarker>()
284    }
285
286    fn create_unhandled_keyboard_event(
287        key: fidl_fuchsia_input::Key,
288        event_type: fidl_fuchsia_ui_input3::KeyEventType,
289        modifiers: Option<fidl_ui_input3::Modifiers>,
290        event_time: zx::MonotonicInstant,
291        device_descriptor: &input_device::InputDeviceDescriptor,
292        keymap: Option<String>,
293    ) -> input_device::UnhandledInputEvent {
294        create_unhandled_keyboard_event_with_key_meaning(
295            key,
296            event_type,
297            modifiers,
298            event_time,
299            device_descriptor,
300            keymap,
301            /* key_meaning */ None,
302        )
303    }
304
305    fn create_unhandled_keyboard_event_with_key_meaning(
306        key: fidl_fuchsia_input::Key,
307        event_type: fidl_fuchsia_ui_input3::KeyEventType,
308        modifiers: Option<fidl_ui_input3::Modifiers>,
309        event_time: zx::MonotonicInstant,
310        device_descriptor: &input_device::InputDeviceDescriptor,
311        keymap: Option<String>,
312        key_meaning: Option<fidl_fuchsia_ui_input3::KeyMeaning>,
313    ) -> input_device::UnhandledInputEvent {
314        input_device::UnhandledInputEvent::try_from(
315            testing_utilities::create_keyboard_event_with_key_meaning(
316                key,
317                event_type,
318                modifiers,
319                event_time,
320                device_descriptor,
321                keymap,
322                key_meaning,
323            ),
324        )
325        .unwrap()
326    }
327
328    fn create_unhandled_input_event(
329        keyboard_event: keyboard_binding::KeyboardEvent,
330        device_descriptor: &input_device::InputDeviceDescriptor,
331        event_time: zx::MonotonicInstant,
332    ) -> input_device::UnhandledInputEvent {
333        input_device::UnhandledInputEvent {
334            device_event: input_device::InputDeviceEvent::Keyboard(keyboard_event),
335            device_descriptor: device_descriptor.clone(),
336            event_time,
337            trace_id: None,
338        }
339    }
340
341    /// Tests that a pressed key event is dispatched.
342    ///
343    /// > NOTE: The `device_descriptor` used in this test case and elsewhere
344    /// *must* be of type `KeyboardDeviceDescriptor` as this is required by the
345    /// pattern matching in `ImeHandler`.
346    #[fasync::run_singlethreaded(test)]
347    async fn pressed_key() {
348        let (proxy, request_stream) = connect_to_key_event_injector();
349        let inspector = fuchsia_inspect::Inspector::default();
350        let test_node = inspector.root().create_child("test_node");
351        let ime_handler =
352            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
353                .await
354                .expect("Failed to create ImeHandler.");
355
356        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
357            keyboard_binding::KeyboardDeviceDescriptor {
358                keys: vec![fidl_input::Key::A],
359                device_id: 0,
360                ..Default::default()
361            },
362        );
363        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
364        let input_events = vec![create_unhandled_keyboard_event(
365            fidl_input::Key::A,
366            fidl_fuchsia_ui_input3::KeyEventType::Pressed,
367            None,
368            event_time_u64,
369            &device_descriptor,
370            /* keymap= */ None,
371        )];
372
373        let expected_events = vec![fidl_ui_input3::KeyEvent {
374            timestamp: Some(event_time_i64),
375            type_: Some(fidl_ui_input3::KeyEventType::Pressed),
376            key: Some(fidl_input::Key::A),
377            // A key "A" without shift is a lowercase 'a'.
378            key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)),
379            device_id: Some(0),
380            ..Default::default()
381        }];
382
383        handle_events(ime_handler, input_events);
384        assert_ime_receives_events(expected_events, request_stream).await;
385    }
386
387    /// Tests that a released key event is dispatched.
388    #[fasync::run_singlethreaded(test)]
389    async fn released_key() {
390        let (proxy, request_stream) = connect_to_key_event_injector();
391        let inspector = fuchsia_inspect::Inspector::default();
392        let test_node = inspector.root().create_child("test_node");
393        let ime_handler =
394            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
395                .await
396                .expect("Failed to create ImeHandler.");
397
398        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
399            keyboard_binding::KeyboardDeviceDescriptor {
400                keys: vec![fidl_input::Key::A],
401                device_id: 0,
402                ..Default::default()
403            },
404        );
405        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
406        let input_events = vec![create_unhandled_keyboard_event(
407            fidl_input::Key::A,
408            fidl_fuchsia_ui_input3::KeyEventType::Released,
409            None,
410            event_time_u64,
411            &device_descriptor,
412            /* keymap= */ None,
413        )];
414
415        let expected_events = vec![fidl_ui_input3::KeyEvent {
416            timestamp: Some(event_time_i64),
417            type_: Some(fidl_ui_input3::KeyEventType::Released),
418            key: Some(fidl_input::Key::A),
419            key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)),
420            device_id: Some(0),
421            ..Default::default()
422        }];
423
424        handle_events(ime_handler, input_events);
425        assert_ime_receives_events(expected_events, request_stream).await;
426    }
427
428    /// Tests that both pressed and released keys are dispatched appropriately.
429    #[fasync::run_singlethreaded(test)]
430    async fn pressed_and_released_key() {
431        let (proxy, request_stream) = connect_to_key_event_injector();
432        let inspector = fuchsia_inspect::Inspector::default();
433        let test_node = inspector.root().create_child("test_node");
434        let ime_handler =
435            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
436                .await
437                .expect("Failed to create ImeHandler.");
438
439        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
440            keyboard_binding::KeyboardDeviceDescriptor {
441                keys: vec![fidl_input::Key::A, fidl_input::Key::B],
442                device_id: 0,
443                ..Default::default()
444            },
445        );
446        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
447        let input_events = vec![
448            create_unhandled_keyboard_event(
449                fidl_input::Key::A,
450                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
451                None,
452                event_time_u64,
453                &device_descriptor,
454                /* keymap= */ None,
455            ),
456            create_unhandled_keyboard_event(
457                fidl_input::Key::A,
458                fidl_fuchsia_ui_input3::KeyEventType::Released,
459                None,
460                event_time_u64,
461                &device_descriptor,
462                /* keymap= */ None,
463            ),
464            create_unhandled_keyboard_event(
465                fidl_input::Key::B,
466                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
467                None,
468                event_time_u64,
469                &device_descriptor,
470                /* keymap= */ None,
471            ),
472            create_unhandled_keyboard_event_with_key_meaning(
473                fidl_input::Key::C,
474                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
475                None,
476                event_time_u64,
477                &device_descriptor,
478                /* keymap= */ None,
479                Some(fidl_fuchsia_ui_input3::KeyMeaning::Codepoint(42)),
480            ),
481        ];
482
483        let expected_events = vec![
484            fidl_ui_input3::KeyEvent {
485                timestamp: Some(event_time_i64),
486                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
487                key: Some(fidl_input::Key::A),
488                key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)),
489                device_id: Some(0),
490                ..Default::default()
491            },
492            fidl_ui_input3::KeyEvent {
493                timestamp: Some(event_time_i64),
494                type_: Some(fidl_ui_input3::KeyEventType::Released),
495                key: Some(fidl_input::Key::A),
496                key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)),
497                device_id: Some(0),
498                ..Default::default()
499            },
500            fidl_ui_input3::KeyEvent {
501                timestamp: Some(event_time_i64),
502                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
503                key: Some(fidl_input::Key::B),
504                key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(98)),
505                device_id: Some(0),
506                ..Default::default()
507            },
508            fidl_ui_input3::KeyEvent {
509                timestamp: Some(event_time_i64),
510                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
511                key: Some(fidl_input::Key::C),
512                key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(42)),
513                device_id: Some(0),
514                ..Default::default()
515            },
516        ];
517
518        handle_events(ime_handler, input_events);
519        assert_ime_receives_events(expected_events, request_stream).await;
520    }
521
522    // Tests that modifier keys are dispatched appropriately.
523    //
524    // This test depends on the incoming event having correct modifier and lock
525    // state.  Typically you'd do this by installing a ModifierHandler upstream
526    // of this pipeline stage.
527    #[fasync::run_singlethreaded(test)]
528    async fn repeated_modifier_key() {
529        let (proxy, request_stream) = connect_to_key_event_injector();
530        let inspector = fuchsia_inspect::Inspector::default();
531        let test_node = inspector.root().create_child("test_node");
532        let ime_handler =
533            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
534                .await
535                .expect("Failed to create ImeHandler.");
536
537        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
538            keyboard_binding::KeyboardDeviceDescriptor {
539                keys: vec![fidl_input::Key::A, fidl_input::Key::CapsLock],
540                device_id: 0,
541                ..Default::default()
542            },
543        );
544        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
545        let input_events = vec![
546            create_unhandled_input_event(
547                KeyboardEvent::new(
548                    fidl_input::Key::CapsLock,
549                    fidl_fuchsia_ui_input3::KeyEventType::Pressed,
550                )
551                .into_with_modifiers(Some(fidl_ui_input3::Modifiers::CAPS_LOCK))
552                .into_with_lock_state(Some(fidl_ui_input3::LockState::CAPS_LOCK)),
553                &device_descriptor,
554                event_time_u64,
555            ),
556            create_unhandled_input_event(
557                KeyboardEvent::new(
558                    fidl_input::Key::A,
559                    fidl_fuchsia_ui_input3::KeyEventType::Pressed,
560                )
561                .into_with_modifiers(Some(fidl_ui_input3::Modifiers::CAPS_LOCK))
562                .into_with_lock_state(Some(fidl_ui_input3::LockState::CAPS_LOCK)),
563                &device_descriptor,
564                event_time_u64,
565            ),
566            create_unhandled_input_event(
567                KeyboardEvent::new(
568                    fidl_input::Key::CapsLock,
569                    fidl_fuchsia_ui_input3::KeyEventType::Released,
570                )
571                .into_with_lock_state(Some(fidl_ui_input3::LockState::CAPS_LOCK)),
572                &device_descriptor,
573                event_time_u64,
574            ),
575        ];
576
577        let expected_events = vec![
578            fidl_ui_input3::KeyEvent {
579                timestamp: Some(event_time_i64),
580                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
581                key: Some(fidl_input::Key::CapsLock),
582                modifiers: Some(fidl_ui_input3::Modifiers::CAPS_LOCK),
583                lock_state: Some(fidl_ui_input3::LockState::CAPS_LOCK),
584                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
585                    fidl_ui_input3::NonPrintableKey::CapsLock,
586                )),
587                device_id: Some(0),
588                ..Default::default()
589            },
590            fidl_ui_input3::KeyEvent {
591                timestamp: Some(event_time_i64),
592                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
593                key: Some(fidl_input::Key::A),
594                modifiers: Some(fidl_ui_input3::Modifiers::CAPS_LOCK),
595                lock_state: Some(fidl_ui_input3::LockState::CAPS_LOCK),
596                key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(65)),
597                device_id: Some(0),
598                ..Default::default()
599            },
600            fidl_ui_input3::KeyEvent {
601                timestamp: Some(event_time_i64),
602                type_: Some(fidl_ui_input3::KeyEventType::Released),
603                key: Some(fidl_input::Key::CapsLock),
604                lock_state: Some(fidl_ui_input3::LockState::CAPS_LOCK),
605                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
606                    fidl_ui_input3::NonPrintableKey::CapsLock,
607                )),
608                device_id: Some(0),
609                ..Default::default()
610            },
611        ];
612
613        handle_events(ime_handler, input_events);
614        assert_ime_receives_events(expected_events, request_stream).await;
615    }
616
617    #[fasync::run_singlethreaded(test)]
618    async fn nonprintable_key_meanings_set_correctly() {
619        let (proxy, request_stream) = connect_to_key_event_injector();
620        let inspector = fuchsia_inspect::Inspector::default();
621        let test_node = inspector.root().create_child("test_node");
622        let ime_handler =
623            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
624                .await
625                .expect("Failed to create ImeHandler.");
626
627        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
628            keyboard_binding::KeyboardDeviceDescriptor {
629                keys: vec![
630                    fidl_input::Key::Enter,
631                    fidl_input::Key::Tab,
632                    fidl_input::Key::Backspace,
633                ],
634                device_id: 0,
635                ..Default::default()
636            },
637        );
638        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
639        let input_events = vec![
640            create_unhandled_keyboard_event(
641                fidl_input::Key::Enter,
642                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
643                None,
644                event_time_u64,
645                &device_descriptor,
646                /* keymap= */ None,
647            ),
648            create_unhandled_keyboard_event(
649                fidl_input::Key::Tab,
650                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
651                None,
652                event_time_u64,
653                &device_descriptor,
654                /* keymap= */ None,
655            ),
656            create_unhandled_keyboard_event(
657                fidl_input::Key::Backspace,
658                fidl_fuchsia_ui_input3::KeyEventType::Released,
659                None,
660                event_time_u64,
661                &device_descriptor,
662                /* keymap= */ None,
663            ),
664        ];
665
666        let expected_events = vec![
667            fidl_ui_input3::KeyEvent {
668                timestamp: Some(event_time_i64),
669                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
670                key: Some(fidl_input::Key::Enter),
671                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
672                    fidl_ui_input3::NonPrintableKey::Enter,
673                )),
674                device_id: Some(0),
675                ..Default::default()
676            },
677            fidl_ui_input3::KeyEvent {
678                timestamp: Some(event_time_i64),
679                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
680                key: Some(fidl_input::Key::Tab),
681                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
682                    fidl_ui_input3::NonPrintableKey::Tab,
683                )),
684                device_id: Some(0),
685                ..Default::default()
686            },
687            fidl_ui_input3::KeyEvent {
688                timestamp: Some(event_time_i64),
689                // Test that things also work when a key is released.
690                type_: Some(fidl_ui_input3::KeyEventType::Released),
691                key: Some(fidl_input::Key::Backspace),
692                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
693                    fidl_ui_input3::NonPrintableKey::Backspace,
694                )),
695                device_id: Some(0),
696                ..Default::default()
697            },
698        ];
699
700        handle_events(ime_handler, input_events);
701        assert_ime_receives_events(expected_events, request_stream).await;
702    }
703
704    #[fasync::run_singlethreaded(test)]
705    async fn tab() {
706        let (proxy, request_stream) = connect_to_key_event_injector();
707        let inspector = fuchsia_inspect::Inspector::default();
708        let test_node = inspector.root().create_child("test_node");
709        let ime_handler =
710            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
711                .await
712                .expect("Failed to create ImeHandler.");
713
714        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
715            keyboard_binding::KeyboardDeviceDescriptor {
716                keys: vec![
717                    fidl_input::Key::Enter,
718                    fidl_input::Key::Tab,
719                    fidl_input::Key::Backspace,
720                ],
721                device_id: 0,
722                ..Default::default()
723            },
724        );
725        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
726        let input_events = vec![create_unhandled_keyboard_event(
727            fidl_input::Key::Tab,
728            fidl_fuchsia_ui_input3::KeyEventType::Pressed,
729            None,
730            event_time_u64,
731            &device_descriptor,
732            /* keymap= */ None,
733        )];
734
735        let expected_events = vec![fidl_ui_input3::KeyEvent {
736            timestamp: Some(event_time_i64),
737            type_: Some(fidl_ui_input3::KeyEventType::Pressed),
738            key: Some(fidl_input::Key::Tab),
739            key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
740                fidl_ui_input3::NonPrintableKey::Tab,
741            )),
742            device_id: Some(0),
743            ..Default::default()
744        }];
745
746        handle_events(ime_handler, input_events);
747        assert_ime_receives_events(expected_events, request_stream).await;
748    }
749
750    #[fasync::run_singlethreaded(test)]
751    async fn shift_shift_a() {
752        let (proxy, request_stream) = connect_to_key_event_injector();
753        let inspector = fuchsia_inspect::Inspector::default();
754        let test_node = inspector.root().create_child("test_node");
755        let ime_handler =
756            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
757                .await
758                .expect("Failed to create ImeHandler.");
759
760        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
761            keyboard_binding::KeyboardDeviceDescriptor {
762                keys: vec![fidl_input::Key::LeftCtrl, fidl_input::Key::Tab],
763                device_id: 0,
764                ..Default::default()
765            },
766        );
767        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
768        let input_events = vec![
769            create_unhandled_keyboard_event(
770                fidl_input::Key::LeftShift,
771                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
772                Some(Modifiers::LEFT_SHIFT | Modifiers::SHIFT),
773                event_time_u64,
774                &device_descriptor,
775                /* keymap= */ None,
776            ),
777            create_unhandled_keyboard_event(
778                fidl_input::Key::RightShift,
779                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
780                Some(Modifiers::LEFT_SHIFT | Modifiers::RIGHT_SHIFT | Modifiers::SHIFT),
781                event_time_u64,
782                &device_descriptor,
783                /* keymap= */ None,
784            ),
785            create_unhandled_keyboard_event(
786                fidl_input::Key::A,
787                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
788                Some(Modifiers::LEFT_SHIFT | Modifiers::RIGHT_SHIFT | Modifiers::SHIFT),
789                event_time_u64,
790                &device_descriptor,
791                /* keymap= */ None,
792            ),
793        ];
794
795        let expected_events = vec![
796            fidl_ui_input3::KeyEvent {
797                timestamp: Some(event_time_i64),
798                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
799                key: Some(fidl_input::Key::LeftShift),
800                modifiers: Some(Modifiers::LEFT_SHIFT | Modifiers::SHIFT),
801                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
802                    fidl_ui_input3::NonPrintableKey::Shift,
803                )),
804                device_id: Some(0),
805                ..Default::default()
806            },
807            fidl_ui_input3::KeyEvent {
808                timestamp: Some(event_time_i64),
809                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
810                key: Some(fidl_input::Key::RightShift),
811                modifiers: Some(Modifiers::RIGHT_SHIFT | Modifiers::LEFT_SHIFT | Modifiers::SHIFT),
812                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
813                    fidl_ui_input3::NonPrintableKey::Shift,
814                )),
815                device_id: Some(0),
816                ..Default::default()
817            },
818            fidl_ui_input3::KeyEvent {
819                timestamp: Some(event_time_i64),
820                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
821                key: Some(fidl_input::Key::A),
822                modifiers: Some(Modifiers::RIGHT_SHIFT | Modifiers::LEFT_SHIFT | Modifiers::SHIFT),
823                key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(65)), // "A"
824                device_id: Some(0),
825                ..Default::default()
826            },
827        ];
828
829        handle_events(ime_handler, input_events);
830        assert_ime_receives_events(expected_events, request_stream).await;
831    }
832
833    #[fasync::run_singlethreaded(test)]
834    async fn ctrl_tab() {
835        let (proxy, request_stream) = connect_to_key_event_injector();
836        let inspector = fuchsia_inspect::Inspector::default();
837        let test_node = inspector.root().create_child("test_node");
838        let ime_handler =
839            ImeHandler::new_handler(proxy, &test_node, metrics::MetricsLogger::default())
840                .await
841                .expect("Failed to create ImeHandler.");
842
843        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
844            keyboard_binding::KeyboardDeviceDescriptor {
845                keys: vec![fidl_input::Key::LeftCtrl, fidl_input::Key::Tab],
846                device_id: 0,
847                ..Default::default()
848            },
849        );
850        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
851        let input_events = vec![
852            create_unhandled_keyboard_event(
853                fidl_input::Key::LeftCtrl,
854                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
855                None,
856                event_time_u64,
857                &device_descriptor,
858                /* keymap= */ None,
859            ),
860            create_unhandled_keyboard_event(
861                fidl_input::Key::Tab,
862                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
863                None,
864                event_time_u64,
865                &device_descriptor,
866                /* keymap= */ None,
867            ),
868        ];
869
870        let expected_events = vec![
871            fidl_ui_input3::KeyEvent {
872                timestamp: Some(event_time_i64),
873                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
874                key: Some(fidl_input::Key::LeftCtrl),
875                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
876                    fidl_ui_input3::NonPrintableKey::Control,
877                )),
878                device_id: Some(0),
879                ..Default::default()
880            },
881            fidl_ui_input3::KeyEvent {
882                timestamp: Some(event_time_i64),
883                type_: Some(fidl_ui_input3::KeyEventType::Pressed),
884                key: Some(fidl_input::Key::Tab),
885                key_meaning: Some(fidl_ui_input3::KeyMeaning::NonPrintableKey(
886                    fidl_ui_input3::NonPrintableKey::Tab,
887                )),
888                device_id: Some(0),
889                ..Default::default()
890            },
891        ];
892
893        handle_events(ime_handler, input_events);
894        assert_ime_receives_events(expected_events, request_stream).await;
895    }
896
897    #[fasync::run_singlethreaded(test)]
898    async fn ime_handler_initialized_with_inspect_node() {
899        let (proxy, _) = connect_to_key_event_injector();
900        let inspector = fuchsia_inspect::Inspector::default();
901        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
902        let _handler =
903            ImeHandler::new_handler(proxy, &fake_handlers_node, metrics::MetricsLogger::default())
904                .await
905                .expect("Failed to create ImeHandler.");
906        diagnostics_assertions::assert_data_tree!(inspector, root: {
907            input_handlers_node: {
908                ime_handler: {
909                    events_received_count: 0u64,
910                    events_handled_count: 0u64,
911                    last_received_timestamp_ns: 0u64,
912                    "fuchsia.inspect.Health": {
913                        status: "STARTING_UP",
914                        // Timestamp value is unpredictable and not relevant in this context,
915                        // so we only assert that the property is present.
916                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
917                    },
918                }
919            }
920        });
921    }
922
923    #[fasync::run_singlethreaded(test)]
924    async fn ime_handler_inspect_counts_events() {
925        let (proxy, _) = connect_to_key_event_injector();
926        let inspector = fuchsia_inspect::Inspector::default();
927        let fake_handlers_node = inspector.root().create_child("input_handlers_node");
928        let ime_handler =
929            ImeHandler::new_handler(proxy, &fake_handlers_node, metrics::MetricsLogger::default())
930                .await
931                .expect("Failed to create ImeHandler.");
932        let device_descriptor = input_device::InputDeviceDescriptor::Keyboard(
933            keyboard_binding::KeyboardDeviceDescriptor {
934                keys: vec![fidl_input::Key::A, fidl_input::Key::B],
935                ..Default::default()
936            },
937        );
938        let (_, event_time_u64) = testing_utilities::event_times();
939        let input_events = vec![
940            testing_utilities::create_keyboard_event_with_time(
941                fidl_input::Key::A,
942                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
943                None,
944                event_time_u64,
945                &device_descriptor,
946                /* keymap= */ None,
947            ),
948            // Should not count received events that have already been handled.
949            testing_utilities::create_keyboard_event_with_handled(
950                fidl_input::Key::B,
951                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
952                None,
953                event_time_u64,
954                &device_descriptor,
955                /* keymap= */ None,
956                /* key_meaning= */ None,
957                input_device::Handled::Yes,
958            ),
959            testing_utilities::create_keyboard_event_with_time(
960                fidl_input::Key::A,
961                fidl_fuchsia_ui_input3::KeyEventType::Released,
962                None,
963                event_time_u64,
964                &device_descriptor,
965                /* keymap= */ None,
966            ),
967            // Should not count non-keyboard input events.
968            testing_utilities::create_fake_input_event(event_time_u64),
969            testing_utilities::create_keyboard_event_with_time(
970                fidl_input::Key::B,
971                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
972                None,
973                event_time_u64,
974                &device_descriptor,
975                /* keymap= */ None,
976            ),
977        ];
978
979        for input_event in input_events {
980            let _ = ime_handler.clone().handle_input_event(input_event).await;
981        }
982
983        let last_event_timestamp: u64 = event_time_u64.into_nanos().try_into().unwrap();
984
985        diagnostics_assertions::assert_data_tree!(inspector, root: {
986            input_handlers_node: {
987                ime_handler: {
988                    events_received_count: 3u64,
989                    events_handled_count: 3u64,
990                    last_received_timestamp_ns: last_event_timestamp,
991                    "fuchsia.inspect.Health": {
992                        status: "STARTING_UP",
993                        // Timestamp value is unpredictable and not relevant in this context,
994                        // so we only assert that the property is present.
995                        start_timestamp_nanos: diagnostics_assertions::AnyProperty
996                    },
997                }
998            }
999        });
1000    }
1001
1002    #[test_case(
1003        keyboard_binding::KeyboardEvent::new(
1004            fidl_input::Key::A,
1005            fidl_ui_input3::KeyEventType::Pressed),
1006        zx::MonotonicInstant::from_nanos(42) => fidl_ui_input3::KeyEvent{
1007            timestamp: Some(42),
1008            type_: Some(fidl_ui_input3::KeyEventType::Pressed),
1009            key: Some(fidl_input::Key::A),
1010            key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)),
1011            device_id: Some(0),
1012            ..Default::default()}; "basic value copy")]
1013    #[test_case(
1014        keyboard_binding::KeyboardEvent::new(
1015            fidl_input::Key::A,
1016            fidl_ui_input3::KeyEventType::Pressed)
1017            .into_with_repeat_sequence(13),
1018        zx::MonotonicInstant::from_nanos(42) => fidl_ui_input3::KeyEvent{
1019            timestamp: Some(42),
1020            type_: Some(fidl_ui_input3::KeyEventType::Pressed),
1021            key: Some(fidl_input::Key::A),
1022            key_meaning: Some(fidl_ui_input3::KeyMeaning::Codepoint(97)),
1023            repeat_sequence: Some(13),
1024            device_id: Some(0),
1025            ..Default::default()}; "repeat_sequence is honored")]
1026    fn test_create_key_event(
1027        event: keyboard_binding::KeyboardEvent,
1028        event_time: zx::MonotonicInstant,
1029    ) -> fidl_ui_input3::KeyEvent {
1030        super::create_key_event(&event, event_time, 0)
1031    }
1032}