Skip to main content

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