Skip to main content

input_testing/
lib.rs

1// Copyright 2022 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 async_utils::event::Event as AsyncEvent;
6use fidl_fuchsia_input::Key;
7use fidl_fuchsia_input_injection::InputDeviceRegistryMarker;
8use fidl_fuchsia_input_report::{
9    ConsumerControlInputReport, ContactInputReport, DeviceInformation, InputReport,
10    KeyboardInputReport, MouseInputReport, TouchInputReport,
11};
12use fidl_fuchsia_math as math;
13use fidl_fuchsia_power_system as fps;
14use fidl_fuchsia_time_alarms as fta;
15use fidl_fuchsia_ui_display_singleton as display_info;
16use fidl_fuchsia_ui_input::KeyboardReport;
17use fidl_fuchsia_ui_test_input::{
18    CoordinateUnit, KeyboardMarker, KeyboardRequest, KeyboardRequestStream,
19    MediaButtonsDeviceMarker, MediaButtonsDeviceRequest, MediaButtonsDeviceRequestStream,
20    MouseMarker, MouseRequest, MouseRequestStream,
21    RegistryRegisterKeyboardAndGetDeviceInfoResponse,
22    RegistryRegisterMediaButtonsDeviceAndGetDeviceInfoResponse,
23    RegistryRegisterMouseAndGetDeviceInfoResponse,
24    RegistryRegisterTouchScreenAndGetDeviceInfoResponse, RegistryRequest, RegistryRequestStream,
25    TouchScreenMarker, TouchScreenRequest, TouchScreenRequestStream,
26};
27use fuchsia_async as fasync;
28use fuchsia_component::client::connect_to_protocol;
29use fuchsia_trace::Scope;
30use futures::stream::FuturesUnordered;
31use futures::{StreamExt, TryStreamExt};
32use keymaps::inverse_keymap::{InverseKeymap, Shift};
33use keymaps::usages::{Usages, hid_usage_to_input3_key};
34use log::{error, info, warn};
35use std::time::Duration;
36
37mod input_device;
38mod input_device_registry;
39mod input_reports_reader;
40
41pub(crate) static INPUT_CATEGORY: &str = "input";
42
43/// Use this to place required DeviceInformation into DeviceDescriptor.
44fn new_fake_device_info() -> DeviceInformation {
45    DeviceInformation {
46        product_id: Some(42),
47        vendor_id: Some(43),
48        version: Some(u32::MAX),
49        polling_rate: Some(1000),
50        ..Default::default()
51    }
52}
53
54/// Converts the `input` string into a key sequence under the `InverseKeymap` derived from `keymap`.
55///
56/// This is intended for end-to-end and input testing only; for production use cases and general
57/// testing, IME injection should be used instead.
58///
59/// A translation from `input` to a sequence of keystrokes is not guaranteed to exist. If a
60/// translation does not exist, `None` is returned.
61///
62/// The sequence does not contain pauses except between repeated keys or to clear a shift state,
63/// though the sequence does terminate with an empty report (no keys pressed). A shift key
64/// transition is sent in advance of each series of keys that needs it.
65///
66/// Note that there is currently no way to distinguish between particular key releases. As such,
67/// only one key release report is generated even in combinations, e.g. Shift + A.
68///
69/// # Example
70///
71/// ```
72/// let key_sequence = derive_key_sequence(&keymaps::US_QWERTY, "A").unwrap();
73///
74/// // [shift, A, clear]
75/// assert_eq!(key_sequence.len(), 3);
76/// ```
77///
78/// TODO(https://fxbug.dev/42059899): Simplify the logic in this test.
79fn derive_key_sequence(keymap: &keymaps::Keymap<'_>, input: &str) -> Option<Vec<KeyboardReport>> {
80    let inverse_keymap = InverseKeymap::new(keymap);
81    let mut reports = vec![];
82    let mut shift_pressed = false;
83    let mut last_usage = None;
84
85    for ch in input.chars() {
86        let key_stroke = inverse_keymap.get(&ch)?;
87
88        match key_stroke.shift {
89            Shift::Yes if !shift_pressed => {
90                shift_pressed = true;
91                last_usage = Some(0);
92            }
93            Shift::No if shift_pressed => {
94                shift_pressed = false;
95                last_usage = Some(0);
96            }
97            _ => {
98                if last_usage == Some(key_stroke.usage) {
99                    last_usage = Some(0);
100                }
101            }
102        }
103
104        if let Some(0) = last_usage {
105            reports.push(KeyboardReport {
106                pressed_keys: if shift_pressed {
107                    vec![Usages::HidUsageKeyLeftShift as u32]
108                } else {
109                    vec![]
110                },
111            });
112        }
113
114        last_usage = Some(key_stroke.usage);
115
116        reports.push(KeyboardReport {
117            pressed_keys: if shift_pressed {
118                vec![key_stroke.usage, Usages::HidUsageKeyLeftShift as u32]
119            } else {
120                vec![key_stroke.usage]
121            },
122        });
123    }
124
125    reports.push(KeyboardReport { pressed_keys: vec![] });
126
127    Some(reports)
128}
129
130fn convert_keyboard_report_to_keys(report: &KeyboardReport) -> Vec<Key> {
131    report
132        .pressed_keys
133        .iter()
134        .map(|&usage| {
135            hid_usage_to_input3_key(usage.try_into().expect("failed to convert usage to u16"))
136                .unwrap_or_else(|| panic!("no Key for usage {:?}", usage))
137        })
138        .collect()
139}
140
141async fn register_touch_screen(
142    mut registry: input_device_registry::InputDeviceRegistry,
143    scope: &fasync::Scope,
144    device_server_end: fidl::endpoints::ServerEnd<TouchScreenMarker>,
145    got_input_reports_reader: AsyncEvent,
146    min_x: i64,
147    max_x: i64,
148    min_y: i64,
149    max_y: i64,
150) -> input_device::DeviceId {
151    let device = registry
152        .add_touchscreen_device(min_x, max_x, min_y, max_y)
153        .await
154        .expect("failed to create fake touchscreen device");
155    let device_id = device.device_id;
156
157    scope.spawn(async move {
158        handle_touchscreen_request_stream(device, device_server_end.into_stream()).await;
159    });
160
161    info!("wait for input-pipeline setup input-reader");
162    let _ = got_input_reports_reader.wait().await;
163    info!("input-pipeline setup input-reader.");
164
165    device_id
166}
167
168async fn register_media_button(
169    mut registry: input_device_registry::InputDeviceRegistry,
170    scope: &fasync::Scope,
171    device_server_end: fidl::endpoints::ServerEnd<MediaButtonsDeviceMarker>,
172    got_input_reports_reader: AsyncEvent,
173) -> input_device::DeviceId {
174    let device = registry
175        .add_media_buttons_device()
176        .await
177        .expect("failed to create fake media buttons device");
178    let device_id = device.device_id;
179
180    let activity_governor = connect_to_protocol::<fps::ActivityGovernorMarker>().ok();
181    let wake_alarm_proxy = connect_to_protocol::<fta::WakeAlarmsMarker>().ok();
182    scope.spawn(async move {
183        handle_media_buttons_device_request_stream(
184            device,
185            device_server_end.into_stream(),
186            activity_governor,
187            wake_alarm_proxy,
188        )
189        .await;
190    });
191
192    info!("wait for input-pipeline setup input-reader");
193    let _ = got_input_reports_reader.wait().await;
194    info!("input-pipeline setup input-reader.");
195
196    device_id
197}
198
199async fn register_keyboard(
200    mut registry: input_device_registry::InputDeviceRegistry,
201    scope: &fasync::Scope,
202    device_server_end: fidl::endpoints::ServerEnd<KeyboardMarker>,
203    got_input_reports_reader: AsyncEvent,
204) -> input_device::DeviceId {
205    let device = registry.add_keyboard_device().await.expect("failed to create fake keyboard");
206    let device_id = device.device_id;
207
208    scope.spawn(async move {
209        handle_keyboard_request_stream(device, device_server_end.into_stream()).await;
210    });
211
212    info!("wait for input-pipeline setup input-reader");
213    let _ = got_input_reports_reader.wait().await;
214    info!("input-pipeline setup input-reader.");
215
216    device_id
217}
218
219async fn register_mouse(
220    mut registry: input_device_registry::InputDeviceRegistry,
221    scope: &fasync::Scope,
222    device_server_end: fidl::endpoints::ServerEnd<MouseMarker>,
223    got_input_reports_reader: AsyncEvent,
224) -> input_device::DeviceId {
225    let device = registry.add_mouse_device().await.expect("failed to create fake mouse");
226    let device_id = device.device_id;
227
228    scope.spawn(async move {
229        handle_mouse_request_stream(device, device_server_end.into_stream()).await;
230    });
231
232    info!("wait for input-pipeline setup input-reader");
233    let _ = got_input_reports_reader.wait().await;
234    info!("input-pipeline setup input-reader.");
235
236    device_id
237}
238
239/// Serves `fuchsia.ui.test.input.Registry`.
240pub async fn handle_registry_request_stream(request_stream: RegistryRequestStream) {
241    request_stream
242        .try_for_each_concurrent(None, |request| async {
243            let input_device_registry = connect_to_protocol::<InputDeviceRegistryMarker>()
244                .expect("connect to input_device_registry");
245            let got_input_reports_reader = AsyncEvent::new();
246
247            let registry = input_device_registry::InputDeviceRegistry::new(
248                input_device_registry,
249                got_input_reports_reader.clone(),
250            );
251            let scope = fasync::Scope::new();
252
253            match request {
254                RegistryRequest::RegisterTouchScreen { payload, responder, .. } => {
255                    info!("register touchscreen");
256                    let device = payload
257                        .device
258                        .expect("no touchscreen device provided in registration request");
259                    let (min_x, max_x, min_y, max_y) = match payload.coordinate_unit {
260                        Some(CoordinateUnit::PhysicalPixels) => {
261                            let display_info_proxy =
262                                connect_to_protocol::<display_info::InfoMarker>()
263                                    .expect("failed to connect to display info service");
264                            let display_dimensions = display_info_proxy
265                                .get_metrics()
266                                .await
267                                .expect("failed to get display metrics")
268                                .extent_in_px
269                                .expect("display metrics missing extent in px");
270                            (
271                                0,
272                                display_dimensions.width as i64,
273                                0,
274                                display_dimensions.height as i64,
275                            )
276                        }
277                        Some(CoordinateUnit::RegisteredDimensions) => {
278                            let dimensions =
279                                payload.display_dimensions.expect("missing dimensions");
280                            if dimensions.min_x >= dimensions.max_x
281                                || dimensions.min_y >= dimensions.max_y
282                            {
283                                panic!("invalid dimensions");
284                            }
285                            (dimensions.min_x, dimensions.max_x, dimensions.min_y, dimensions.max_y)
286                        }
287                        _ => (-1000, 1000, -1000, 1000),
288                    };
289
290                    register_touch_screen(
291                        registry,
292                        &scope,
293                        device,
294                        got_input_reports_reader,
295                        min_x,
296                        max_x,
297                        min_y,
298                        max_y,
299                    )
300                    .await;
301
302                    responder.send().expect("Failed to respond to RegisterTouchScreen request");
303                }
304                RegistryRequest::RegisterTouchScreenAndGetDeviceInfo {
305                    payload, responder, ..
306                } => {
307                    info!("register touchscreen");
308                    let device = payload
309                        .device
310                        .expect("no touchscreen device provided in registration request");
311                    let (min_x, max_x, min_y, max_y) = match payload.coordinate_unit {
312                        Some(CoordinateUnit::PhysicalPixels) => {
313                            let display_info_proxy =
314                                connect_to_protocol::<display_info::InfoMarker>()
315                                    .expect("failed to connect to display info service");
316                            let display_dimensions = display_info_proxy
317                                .get_metrics()
318                                .await
319                                .expect("failed to get display metrics")
320                                .extent_in_px
321                                .expect("display metrics missing extent in px");
322                            (
323                                0,
324                                display_dimensions.width as i64,
325                                0,
326                                display_dimensions.height as i64,
327                            )
328                        }
329                        Some(CoordinateUnit::RegisteredDimensions) => {
330                            let dimensions =
331                                payload.display_dimensions.expect("missing dimensions");
332                            if dimensions.min_x >= dimensions.max_x
333                                || dimensions.min_y >= dimensions.max_y
334                            {
335                                panic!("invalid dimensions");
336                            }
337                            (dimensions.min_x, dimensions.max_x, dimensions.min_y, dimensions.max_y)
338                        }
339                        _ => (-1000, 1000, -1000, 1000),
340                    };
341
342                    let device_id = register_touch_screen(
343                        registry,
344                        &scope,
345                        device,
346                        got_input_reports_reader,
347                        min_x,
348                        max_x,
349                        min_y,
350                        max_y,
351                    )
352                    .await;
353
354                    responder
355                        .send(RegistryRegisterTouchScreenAndGetDeviceInfoResponse {
356                            device_id: Some(device_id),
357                            ..Default::default()
358                        })
359                        .expect("Failed to respond to RegisterTouchScreenAndGetDeviceInfo request");
360                }
361                RegistryRequest::RegisterMediaButtonsDevice { payload, responder, .. } => {
362                    info!("register media buttons device");
363                    let device = payload
364                        .device
365                        .expect("no media buttons device provided in registration request");
366
367                    register_media_button(registry, &scope, device, got_input_reports_reader).await;
368
369                    responder
370                        .send()
371                        .expect("Failed to respond to RegisterMediaButtonsDevice request");
372                }
373                RegistryRequest::RegisterMediaButtonsDeviceAndGetDeviceInfo {
374                    payload,
375                    responder,
376                    ..
377                } => {
378                    info!("register media buttons device");
379                    let device = payload
380                        .device
381                        .expect("no media buttons device provided in registration request");
382
383                    let device_id =
384                        register_media_button(registry, &scope, device, got_input_reports_reader)
385                            .await;
386
387                    responder.send(RegistryRegisterMediaButtonsDeviceAndGetDeviceInfoResponse {
388                        device_id: Some(device_id),
389                        ..Default::default()
390                    }).expect(
391                        "Failed to respond to RegisterMediaButtonsDeviceAndGetDeviceInfo request",
392                    );
393                }
394                RegistryRequest::RegisterKeyboard { payload, responder, .. } => {
395                    info!("register keyboard device");
396                    let device = payload
397                        .device
398                        .expect("no keyboard device provided in registration request");
399
400                    register_keyboard(registry, &scope, device, got_input_reports_reader).await;
401
402                    responder.send().expect("Failed to respond to RegisterKeyboard request");
403                }
404                RegistryRequest::RegisterKeyboardAndGetDeviceInfo {
405                    payload, responder, ..
406                } => {
407                    info!("register keyboard device");
408                    let device = payload
409                        .device
410                        .expect("no keyboard device provided in registration request");
411
412                    let device_id =
413                        register_keyboard(registry, &scope, device, got_input_reports_reader).await;
414
415                    responder
416                        .send(RegistryRegisterKeyboardAndGetDeviceInfoResponse {
417                            device_id: Some(device_id),
418                            ..Default::default()
419                        })
420                        .expect("Failed to respond to RegisterKeyboardAndGetDeviceInfo request");
421                }
422                RegistryRequest::RegisterMouse { payload, responder } => {
423                    info!("register mouse device");
424                    let device = payload.device.expect("no mouse provided in registration request");
425
426                    register_mouse(registry, &scope, device, got_input_reports_reader).await;
427
428                    responder.send().expect("Failed to respond to RegisterMouse request");
429                }
430                RegistryRequest::RegisterMouseAndGetDeviceInfo { payload, responder } => {
431                    info!("register mouse device");
432                    let device = payload.device.expect("no mouse provided in registration request");
433
434                    let device_id =
435                        register_mouse(registry, &scope, device, got_input_reports_reader).await;
436
437                    responder
438                        .send(RegistryRegisterMouseAndGetDeviceInfoResponse {
439                            device_id: Some(device_id),
440                            ..Default::default()
441                        })
442                        .expect("Failed to respond to RegisterMouse request");
443                }
444            }
445
446            scope.join().await;
447            Ok(())
448        })
449        .await
450        .expect("failed to serve test realm factory request stream");
451}
452
453fn input_report_for_touch_contacts(
454    contacts: Vec<(u32, math::Vec_)>,
455    trace_id: Option<u64>,
456) -> InputReport {
457    let contact_input_reports = contacts
458        .into_iter()
459        .map(|(contact_id, location)| ContactInputReport {
460            contact_id: Some(contact_id),
461            position_x: Some(location.x as i64),
462            position_y: Some(location.y as i64),
463            contact_width: Some(0),
464            contact_height: Some(0),
465            ..Default::default()
466        })
467        .collect();
468
469    let touch_input_report = TouchInputReport {
470        contacts: Some(contact_input_reports),
471        pressed_buttons: Some(vec![]),
472        ..Default::default()
473    };
474
475    InputReport {
476        event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
477        touch: Some(touch_input_report),
478        trace_id,
479        ..Default::default()
480    }
481}
482
483/// Serves `fuchsia.ui.test.input.TouchScreen`.
484async fn handle_touchscreen_request_stream(
485    touchscreen_device: input_device::InputDevice,
486    mut request_stream: TouchScreenRequestStream,
487) {
488    while let Some(request) = request_stream.next().await {
489        match request {
490            Ok(TouchScreenRequest::SimulateTap { payload, responder }) => {
491                let tap_location = payload.tap_location.expect("missing tap location");
492                {
493                    fuchsia_trace::duration!("input", "simulate_tap_down");
494                    let trace_id = fuchsia_trace::Id::new();
495                    fuchsia_trace::flow_begin!("input", "input_report", trace_id);
496                    touchscreen_device
497                        .send_input_report(input_report_for_touch_contacts(
498                            vec![(1, tap_location)],
499                            Some(trace_id.into()),
500                        ))
501                        .expect("Failed to send touch down input report");
502                }
503
504                // Send a report with an empty set of touch contacts, so that input
505                // pipeline generates a pointer event with phase == UP.
506                fuchsia_trace::duration!("input", "simulate_tap_up");
507                let trace_id = fuchsia_trace::Id::new();
508                fuchsia_trace::flow_begin!("input", "input_report", trace_id);
509                touchscreen_device
510                    .send_input_report(input_report_for_touch_contacts(
511                        vec![],
512                        Some(trace_id.into()),
513                    ))
514                    .expect("failed to send touch up input report");
515
516                responder.send().expect("Failed to send SimulateTap response");
517            }
518            Ok(TouchScreenRequest::SimulateSwipe { payload, responder }) => {
519                // Compute the x- and y- displacements between successive touch events.
520                let start_location = payload.start_location.expect("missing start location");
521                let end_location = payload.end_location.expect("missing end location");
522                let move_event_count = payload.move_event_count.expect("missing move event count");
523                assert_ne!(move_event_count, 0);
524                let duration_nanos = payload.duration.unwrap_or(0);
525                let delay_nanos = duration_nanos / ((move_event_count as i64) + 1);
526                let delay = fasync::MonotonicDuration::from_nanos(delay_nanos);
527
528                let start_x_f = start_location.x as f64;
529                let start_y_f = start_location.y as f64;
530                let end_x_f = end_location.x as f64;
531                let end_y_f = end_location.y as f64;
532                let move_event_count_f = move_event_count as f64;
533                let step_size_x = (end_x_f - start_x_f) / move_event_count_f;
534                let step_size_y = (end_y_f - start_y_f) / move_event_count_f;
535
536                // Generate an event at `start_location`, followed by `move_event_count - 1`
537                // evenly-spaced events, followed by an event at `end_location`.
538                for i in 0..move_event_count + 1 {
539                    let i_f = i as f64;
540                    let event_x = start_x_f + (i_f * step_size_x);
541                    let event_y = start_y_f + (i_f * step_size_y);
542
543                    {
544                        fuchsia_trace::duration!("input", "simulate_swipe_move", "idx"=>i);
545                        let trace_id = fuchsia_trace::Id::new();
546                        fuchsia_trace::flow_begin!("input", "input_report", trace_id);
547
548                        touchscreen_device
549                            .send_input_report(input_report_for_touch_contacts(
550                                vec![(1, math::Vec_ { x: event_x as i32, y: event_y as i32 })],
551                                Some(trace_id.into()),
552                            ))
553                            .expect("Failed to send tap input report");
554                    }
555                    fasync::Timer::new(delay).await;
556                }
557
558                // Send a report with an empty set of touch contacts, so that input
559                // pipeline generates a pointer event with phase == UP.
560                fuchsia_trace::duration!("input", "simulate_swipe_up");
561                let trace_id = fuchsia_trace::Id::new();
562                fuchsia_trace::flow_begin!("input", "input_report", trace_id);
563                touchscreen_device
564                    .send_input_report(input_report_for_touch_contacts(
565                        vec![],
566                        Some(trace_id.into()),
567                    ))
568                    .expect("failed to send empty input report");
569
570                responder.send().expect("Failed to send SimulateSwipe response");
571            }
572            Ok(TouchScreenRequest::SimulateMultiTap { payload, responder }) => {
573                let tap_locations = payload.tap_locations.expect("missing tap locations");
574
575                {
576                    fuchsia_trace::duration!("input", "simulate_multi_tap_down");
577                    let trace_id = fuchsia_trace::Id::new();
578                    fuchsia_trace::flow_begin!("input", "input_report", trace_id);
579                    touchscreen_device
580                        .send_input_report(input_report_for_touch_contacts(
581                            tap_locations
582                                .into_iter()
583                                .enumerate()
584                                .map(|(i, it)| (i as u32, it))
585                                .collect(),
586                            Some(trace_id.into()),
587                        ))
588                        .expect("Failed to send tap input report");
589                }
590
591                // Send a report with an empty set of touch contacts, so that input
592                // pipeline generates a pointer event with phase == UP.
593                fuchsia_trace::duration!("input", "simulate_multi_tap_up");
594                let trace_id = fuchsia_trace::Id::new();
595                fuchsia_trace::flow_begin!("input", "input_report", trace_id);
596                touchscreen_device
597                    .send_input_report(input_report_for_touch_contacts(
598                        vec![],
599                        Some(trace_id.into()),
600                    ))
601                    .expect("failed to send empty input report");
602                responder.send().expect("Failed to send SimulateMultiTap response");
603            }
604            Ok(TouchScreenRequest::SimulateMultiFingerGesture { payload, responder }) => {
605                // Compute the x- and y- displacements between successive touch events.
606                let start_locations = payload.start_locations.expect("missing start locations");
607                let end_locations = payload.end_locations.expect("missing end locations");
608                let move_event_count = payload.move_event_count.expect("missing move event count");
609                let finger_count = payload.finger_count.expect("missing finger count") as usize;
610
611                let move_event_count_f = move_event_count as f32;
612
613                let mut steps: Vec<math::VecF> = vec![];
614
615                for finger in 0..finger_count {
616                    let start_x = start_locations[finger].x as f32;
617                    let start_y = start_locations[finger].y as f32;
618                    let end_x = end_locations[finger].x as f32;
619                    let end_y = end_locations[finger].y as f32;
620                    let step_x = (end_x - start_x) / move_event_count_f;
621                    let step_y = (end_y - start_y) / move_event_count_f;
622                    steps.push(math::VecF { x: step_x, y: step_y });
623                }
624
625                // Generate an event at `start_location`, followed by `move_event_count - 1`
626                // evenly-spaced events, followed by an event at `end_location`.
627                for i in 0..move_event_count {
628                    let i_f = i as f32;
629
630                    let mut contacts: Vec<(u32, math::Vec_)> = vec![];
631
632                    for finger in 0..finger_count {
633                        let start_x = start_locations[finger].x as f32;
634                        let start_y = start_locations[finger].y as f32;
635                        let event_x = (start_x + i_f * steps[finger].x) as i32;
636                        let event_y = (start_y + i_f * steps[finger].y) as i32;
637                        contacts.push((finger as u32, math::Vec_ { x: event_x, y: event_y }));
638                    }
639
640                    fuchsia_trace::duration!("input", "simulate_multi_finger_gesture_move", "idx"=>i);
641                    let trace_id = fuchsia_trace::Id::new();
642                    fuchsia_trace::flow_begin!("input", "input_report", trace_id);
643
644                    touchscreen_device
645                        .send_input_report(input_report_for_touch_contacts(
646                            contacts,
647                            Some(trace_id.into()),
648                        ))
649                        .expect("Failed to send tap input report");
650                }
651
652                // Send a report with an empty set of touch contacts, so that input
653                // pipeline generates a pointer event with phase == UP.
654                fuchsia_trace::duration!("input", "simulate_multi_finger_gesture_up");
655                let trace_id = fuchsia_trace::Id::new();
656                fuchsia_trace::flow_begin!("input", "input_report", trace_id);
657                touchscreen_device
658                    .send_input_report(input_report_for_touch_contacts(
659                        vec![],
660                        Some(trace_id.into()),
661                    ))
662                    .expect("failed to send empty input report");
663
664                responder.send().expect("Failed to send SimulateMultiFingerGesture response");
665            }
666            Ok(TouchScreenRequest::SimulateTouchEvent { report, responder }) => {
667                fuchsia_trace::duration!("input", "simulate_touch_event");
668                let trace_id = fuchsia_trace::Id::new();
669                fuchsia_trace::flow_begin!("input", "input_report", trace_id);
670                let touch_report = fidl_fuchsia_input_report::TouchInputReport {
671                    contacts: report.contacts.map(|contacts| {
672                        contacts
673                            .into_iter()
674                            .map(|c| fidl_fuchsia_input_report::ContactInputReport {
675                                contact_id: c.contact_id,
676                                position_x: c.position_x,
677                                position_y: c.position_y,
678                                ..Default::default()
679                            })
680                            .collect()
681                    }),
682                    ..Default::default()
683                };
684                let input_report = InputReport {
685                    event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
686                    touch: Some(touch_report),
687                    trace_id: Some(trace_id.into()),
688                    ..Default::default()
689                };
690                touchscreen_device
691                    .send_input_report(input_report)
692                    .expect("failed to send empty input report");
693                responder.send().expect("Failed to send SimulateTouchEvent response");
694            }
695            Err(e) => {
696                error!("Error on touchscreen channel: {}", e);
697                return;
698            }
699        }
700    }
701}
702
703/// Serves `fuchsia.ui.test.input.MediaButtonsDevice`.
704async fn handle_media_buttons_device_request_stream(
705    media_buttons_device: input_device::InputDevice,
706    request_stream: MediaButtonsDeviceRequestStream,
707    activity_governor: Option<fps::ActivityGovernorProxy>,
708    wake_alarm_proxy: Option<fta::WakeAlarmsProxy>,
709) {
710    let mut request_stream = request_stream.fuse();
711    let (alarm_sender, mut alarm_receiver) = futures::channel::mpsc::unbounded();
712
713    // Incomplete tasks are dropped when this function exits, avoiding detached orphan tasks.
714    let mut scheduled_inputs = FuturesUnordered::new();
715
716    loop {
717        futures::select! {
718            request = request_stream.next() => {
719                match request {
720                    Some(Ok(MediaButtonsDeviceRequest::SimulateButtonPress { payload, responder })) => {
721                        if let Some(button) = payload.button {
722                            let button = fidl_fuchsia_input::ConsumerControlButton::from_primitive(button.into_primitive()).unwrap();
723                            let wake_lease = if let Some(ref ag) = activity_governor {
724                                acquire_and_deposit_lease(ag, "input-helper-button").await
725                            } else {
726                                None
727                            };
728                            send_media_button_press_and_release(&media_buttons_device, button, wake_lease);
729                            responder.send().expect("Failed to send SimulateButtonPress response");
730                        } else {
731                            warn!("SimulateButtonPress request missing button");
732                            let _ = responder.send();
733                        }
734                    }
735                    Some(Ok(MediaButtonsDeviceRequest::SendButtonsState { payload, responder })) => {
736                        let buttons = match payload.buttons {
737                            Some(buttons) => buttons.into_iter().map(|b| fidl_fuchsia_input::ConsumerControlButton::from_primitive(b.into_primitive()).unwrap()).collect(),
738                            None => vec![],
739                        };
740                        let wake_lease = if let Some(ref ag) = activity_governor {
741                            acquire_and_deposit_lease(ag, "input-helper-button-state").await
742                        } else {
743                            None
744                        };
745                        let input_report = InputReport {
746                            event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
747                            consumer_control: Some(ConsumerControlInputReport {
748                                pressed_buttons: Some(buttons),
749                                ..Default::default()
750                            }),
751                            wake_lease,
752                            ..Default::default()
753                        };
754
755                        media_buttons_device
756                            .send_input_report(input_report)
757                            .expect("Failed to send button press input report");
758
759                        responder.send().expect("Failed to send SimulateButtonPress response");
760                    }
761
762                    Some(Ok(MediaButtonsDeviceRequest::ScheduleSimulateButtonPress { payload, responder })) => {
763                        let button = fidl_fuchsia_input::ConsumerControlButton::from_primitive(payload.button.expect("missing button").into_primitive()).unwrap();
764                        let delay = zx::Duration::from_nanos(payload.delay.expect("missing delay"));
765                        let sender = alarm_sender.clone();
766                        let proxy = wake_alarm_proxy.clone();
767
768                        scheduled_inputs.push(
769                        fasync::Task::local(async move {
770                            if let Some(alarm_proxy) = proxy {
771                                // Use boot instant here since it will increase,
772                                // even when the device is suspended.
773                                let time = zx::BootInstant::after(delay);
774                                info!("scheduling button press for {:?}", time);
775                                fuchsia_trace::instant!(
776                                    INPUT_CATEGORY,
777                                    "WakeupTest:TimerSet",
778                                    Scope::Process
779                                );
780                                let (_lease, peer) = zx::EventPair::create();
781                                match alarm_proxy
782                                    .set_and_wait(time, fta::SetMode::KeepAlive(peer), "input_helper_button")
783                                    .await
784                                {
785                                    Ok(Ok(lease_token)) => {
786                                        let _ = sender.unbounded_send(Some((button, lease_token)));
787                                    }
788                                    Ok(Err(e)) => {
789                                        error!("Failed to set wakeup alarm: {:?}", e);
790                                    }
791                                    Err(e) => {
792                                        error!("FIDL error: {:?}", e);
793                                    }
794                                }
795                            } else {
796                                error!("failed to connect to WakeAlarms protocol");
797                            }
798                        }));
799                        responder.send().expect("Failed to send ScheduleSimulateButtonPress response");
800                    }
801                    Some(Err(e)) => {
802                        error!("Error on media buttons device channel: {}", e);
803                        break;
804                    }
805                    None => break,
806                }
807            },
808            // while processing the request stream, keep checking for scheduled tasks that sent the
809            // fired alarm info.
810            fired_alarm = alarm_receiver.next() => {
811                if let Some(Some((button, lease_token))) = fired_alarm {
812                    info!("wake alarm fired, sending button event");
813                    // Trace how long we spend processing the timer. It should be inconsequential to the resume
814                    // latency.
815                    fuchsia_trace::duration!(INPUT_CATEGORY,"WakeupTest:OnTimer");
816
817                    // Duplicate for local deposit to mimic hardware safety timeout.
818                    if let Ok(local_lease) = lease_token.duplicate_handle(zx::Rights::SAME_RIGHTS) {
819                        deposit_wake_lease(local_lease, zx::MonotonicDuration::from_millis(100));
820                    }
821
822                    send_media_button_press_and_release(&media_buttons_device, button, Some(lease_token));
823                }
824            },
825            _ = scheduled_inputs.select_next_some() => {},
826        }
827    }
828}
829
830fn deposit_wake_lease(lease: zx::EventPair, duration: zx::MonotonicDuration) {
831    fasync::Task::local(async move {
832        fasync::Timer::new(fasync::MonotonicInstant::after(duration)).await;
833        std::mem::drop(lease);
834    })
835    .detach();
836}
837
838/// Helper to acquire a wake lease and deposit a local safety copy.
839async fn acquire_and_deposit_lease(
840    ag: &fps::ActivityGovernorProxy,
841    name: &str,
842) -> Option<zx::EventPair> {
843    match ag.acquire_wake_lease(name).await {
844        Ok(Ok(lease)) => {
845            if let Ok(local_lease) = lease.duplicate_handle(zx::Rights::SAME_RIGHTS) {
846                deposit_wake_lease(local_lease, zx::MonotonicDuration::from_millis(100));
847            }
848            Some(lease)
849        }
850        Ok(Err(e)) => {
851            error!("Failed to acquire wake lease {name}: {:?}", e);
852            None
853        }
854        Err(e) => {
855            error!("FIDL error acquiring wake lease {name}: {:?}", e);
856            None
857        }
858    }
859}
860
861fn send_media_button_press_and_release(
862    media_buttons_device: &input_device::InputDevice,
863    button: fidl_fuchsia_input::ConsumerControlButton,
864    wake_lease: Option<zx::EventPair>,
865) {
866    let media_buttons_input_report =
867        ConsumerControlInputReport { pressed_buttons: Some(vec![button]), ..Default::default() };
868
869    let release_wake_lease =
870        wake_lease.as_ref().and_then(|lease| lease.duplicate_handle(zx::Rights::SAME_RIGHTS).ok());
871
872    let input_report = InputReport {
873        event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
874        consumer_control: Some(media_buttons_input_report),
875        wake_lease,
876        ..Default::default()
877    };
878
879    media_buttons_device
880        .send_input_report(input_report)
881        .expect("Failed to send button press input report");
882
883    // Send a report with an empty set of pressed buttons,
884    // so that input pipeline generates a media buttons
885    // event with the target button being released.
886    let empty_report = InputReport {
887        event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
888        consumer_control: Some(ConsumerControlInputReport {
889            pressed_buttons: Some(vec![]),
890            ..Default::default()
891        }),
892        wake_lease: release_wake_lease,
893        ..Default::default()
894    };
895
896    media_buttons_device
897        .send_input_report(empty_report)
898        .expect("Failed to send button release input report");
899}
900
901/// Serves `fuchsia.ui.test.input.Keyboard`.
902async fn handle_keyboard_request_stream(
903    keyboard_device: input_device::InputDevice,
904    mut request_stream: KeyboardRequestStream,
905) {
906    while let Some(request) = request_stream.next().await {
907        match request {
908            Ok(KeyboardRequest::SimulateUsAsciiTextEntry { payload, responder }) => {
909                if let Some(text) = payload.text {
910                    let key_sequence = derive_key_sequence(&keymaps::US_QWERTY, &text)
911                        .expect("Failed to derive key sequence");
912
913                    let mut key_iter = key_sequence.into_iter().peekable();
914                    while let Some(keyboard_report) = key_iter.next() {
915                        let input_report = InputReport {
916                            event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
917                            keyboard: Some(KeyboardInputReport {
918                                pressed_keys3: Some(convert_keyboard_report_to_keys(
919                                    &keyboard_report,
920                                )),
921                                ..Default::default()
922                            }),
923                            ..Default::default()
924                        };
925
926                        keyboard_device
927                            .send_input_report(input_report)
928                            .expect("Failed to send key event report");
929
930                        if key_iter.peek().is_some() {
931                            fuchsia_async::Timer::new(Duration::from_millis(100)).await;
932                        }
933                    }
934
935                    responder.send().expect("Failed to send SimulateTextEntry response");
936                } else {
937                    warn!("SimulateTextEntry request missing text");
938                }
939            }
940            Ok(KeyboardRequest::SimulateKeyEvent { payload, responder }) => {
941                let keyboard_report = fidl_fuchsia_input_report::KeyboardInputReport {
942                    pressed_keys3: payload.pressed_keys,
943                    ..Default::default()
944                };
945                let input_report = InputReport {
946                    event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
947                    keyboard: Some(keyboard_report),
948                    ..Default::default()
949                };
950
951                keyboard_device
952                    .send_input_report(input_report)
953                    .expect("Failed to send key event report");
954
955                responder.send().expect("Failed to send SimulateKeyEvent response");
956            }
957            Ok(KeyboardRequest::SimulateKeyPress { payload, responder }) => {
958                let key_code = payload.key_code.expect("no key code");
959
960                let down_report = InputReport {
961                    event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
962                    keyboard: Some(KeyboardInputReport {
963                        pressed_keys3: Some(vec![key_code]),
964                        ..Default::default()
965                    }),
966                    ..Default::default()
967                };
968
969                let up_report = InputReport {
970                    event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
971                    keyboard: Some(KeyboardInputReport {
972                        pressed_keys3: Some(vec![]),
973                        ..Default::default()
974                    }),
975                    ..Default::default()
976                };
977
978                keyboard_device.send_input_report(down_report).expect("Failed to send key down");
979
980                keyboard_device.send_input_report(up_report).expect("Failed to send key up");
981
982                responder.send().expect("Failed to send SimulateKeyPress response");
983            }
984            Err(e) => {
985                error!("Error on keyboard device channel: {}", e);
986                return;
987            }
988        }
989    }
990}
991
992/// Serves `fuchsia.ui.test.input.Mouse`.
993async fn handle_mouse_request_stream(
994    mouse_device: input_device::InputDevice,
995    mut request_stream: MouseRequestStream,
996) {
997    while let Some(request) = request_stream.next().await {
998        match request {
999            Ok(MouseRequest::SimulateMouseEvent { payload, responder }) => {
1000                let mut mouse_input_report = MouseInputReport {
1001                    movement_x: payload.movement_x,
1002                    movement_y: payload.movement_y,
1003                    scroll_v: payload.scroll_v_detent,
1004                    scroll_h: payload.scroll_h_detent,
1005                    ..Default::default()
1006                };
1007                if let Some(pressed_buttons) = payload.pressed_buttons {
1008                    mouse_input_report.pressed_buttons = Some(
1009                        pressed_buttons
1010                            .into_iter()
1011                            .map(|b| {
1012                                b.into_primitive()
1013                                    .try_into()
1014                                    .expect("failed to convert MouseButton to u8")
1015                            })
1016                            .collect(),
1017                    );
1018                }
1019
1020                let input_report = InputReport {
1021                    event_time: Some(fasync::MonotonicInstant::now().into_nanos()),
1022                    mouse: Some(mouse_input_report),
1023                    ..Default::default()
1024                };
1025
1026                mouse_device
1027                    .send_input_report(input_report)
1028                    .expect("Failed to send key event report");
1029
1030                responder.send().expect("Failed to send SimulateMouseEvent response");
1031            }
1032            Err(e) => {
1033                error!("Error on keyboard device channel: {}", e);
1034                return;
1035            }
1036        }
1037    }
1038}
1039
1040#[cfg(test)]
1041mod tests {
1042    // Most of the functions in this file need to bind to FIDL services in
1043    // this component's environment to do their work, but a component can't
1044    // modify its own environment. Hence, we can't validate those functions.
1045    //
1046    // However, we can (and do) validate derive_key_sequence().
1047
1048    use super::{
1049        KeyboardReport, Usages, derive_key_sequence, handle_media_buttons_device_request_stream,
1050    };
1051    use pretty_assertions::assert_eq;
1052
1053    // TODO(https://fxbug.dev/42059899): Remove this macro.
1054    macro_rules! reports {
1055        ( $( [ $( $usages:expr ),* ] ),* $( , )? ) => {
1056            Some(vec![
1057                $(
1058                    KeyboardReport {
1059                        pressed_keys: vec![$($usages as u32),*]
1060                    }
1061                ),*
1062            ])
1063        }
1064    }
1065
1066    #[test]
1067    fn lowercase() {
1068        assert_eq!(
1069            derive_key_sequence(&keymaps::US_QWERTY, "lowercase"),
1070            reports![
1071                [Usages::HidUsageKeyL],
1072                [Usages::HidUsageKeyO],
1073                [Usages::HidUsageKeyW],
1074                [Usages::HidUsageKeyE],
1075                [Usages::HidUsageKeyR],
1076                [Usages::HidUsageKeyC],
1077                [Usages::HidUsageKeyA],
1078                [Usages::HidUsageKeyS],
1079                [Usages::HidUsageKeyE],
1080                [],
1081            ]
1082        );
1083    }
1084
1085    #[test]
1086    fn numerics() {
1087        assert_eq!(
1088            derive_key_sequence(&keymaps::US_QWERTY, "0123456789"),
1089            reports![
1090                [Usages::HidUsageKey0],
1091                [Usages::HidUsageKey1],
1092                [Usages::HidUsageKey2],
1093                [Usages::HidUsageKey3],
1094                [Usages::HidUsageKey4],
1095                [Usages::HidUsageKey5],
1096                [Usages::HidUsageKey6],
1097                [Usages::HidUsageKey7],
1098                [Usages::HidUsageKey8],
1099                [Usages::HidUsageKey9],
1100                [],
1101            ]
1102        );
1103    }
1104
1105    #[test]
1106    fn internet_text_entry() {
1107        assert_eq!(
1108            derive_key_sequence(&keymaps::US_QWERTY, "http://127.0.0.1:8080"),
1109            reports![
1110                [Usages::HidUsageKeyH],
1111                [Usages::HidUsageKeyT],
1112                [],
1113                [Usages::HidUsageKeyT],
1114                [Usages::HidUsageKeyP],
1115                // ':'
1116                // Shift is actuated first on its own, then together with
1117                // the key.
1118                [Usages::HidUsageKeyLeftShift],
1119                [Usages::HidUsageKeySemicolon, Usages::HidUsageKeyLeftShift],
1120                [],
1121                [Usages::HidUsageKeySlash],
1122                [],
1123                [Usages::HidUsageKeySlash],
1124                [Usages::HidUsageKey1],
1125                [Usages::HidUsageKey2],
1126                [Usages::HidUsageKey7],
1127                [Usages::HidUsageKeyDot],
1128                [Usages::HidUsageKey0],
1129                [Usages::HidUsageKeyDot],
1130                [Usages::HidUsageKey0],
1131                [Usages::HidUsageKeyDot],
1132                [Usages::HidUsageKey1],
1133                [Usages::HidUsageKeyLeftShift],
1134                [Usages::HidUsageKeySemicolon, Usages::HidUsageKeyLeftShift],
1135                [],
1136                [Usages::HidUsageKey8],
1137                [Usages::HidUsageKey0],
1138                [Usages::HidUsageKey8],
1139                [Usages::HidUsageKey0],
1140                [],
1141            ]
1142        );
1143    }
1144
1145    #[test]
1146    fn sentence() {
1147        assert_eq!(
1148            derive_key_sequence(&keymaps::US_QWERTY, "Hello, world!"),
1149            reports![
1150                [Usages::HidUsageKeyLeftShift],
1151                [Usages::HidUsageKeyH, Usages::HidUsageKeyLeftShift],
1152                [],
1153                [Usages::HidUsageKeyE],
1154                [Usages::HidUsageKeyL],
1155                [],
1156                [Usages::HidUsageKeyL],
1157                [Usages::HidUsageKeyO],
1158                [Usages::HidUsageKeyComma],
1159                [Usages::HidUsageKeySpace],
1160                [Usages::HidUsageKeyW],
1161                [Usages::HidUsageKeyO],
1162                [Usages::HidUsageKeyR],
1163                [Usages::HidUsageKeyL],
1164                [Usages::HidUsageKeyD],
1165                [Usages::HidUsageKeyLeftShift],
1166                [Usages::HidUsageKey1, Usages::HidUsageKeyLeftShift],
1167                [],
1168            ]
1169        );
1170    }
1171
1172    #[test]
1173    fn hold_shift() {
1174        assert_eq!(
1175            derive_key_sequence(&keymaps::US_QWERTY, "ALL'S WELL!"),
1176            reports![
1177                [Usages::HidUsageKeyLeftShift],
1178                [Usages::HidUsageKeyA, Usages::HidUsageKeyLeftShift],
1179                [Usages::HidUsageKeyL, Usages::HidUsageKeyLeftShift],
1180                [Usages::HidUsageKeyLeftShift],
1181                [Usages::HidUsageKeyL, Usages::HidUsageKeyLeftShift],
1182                [],
1183                [Usages::HidUsageKeyApostrophe],
1184                [Usages::HidUsageKeyLeftShift],
1185                [Usages::HidUsageKeyS, Usages::HidUsageKeyLeftShift],
1186                [Usages::HidUsageKeySpace, Usages::HidUsageKeyLeftShift],
1187                [Usages::HidUsageKeyW, Usages::HidUsageKeyLeftShift],
1188                [Usages::HidUsageKeyE, Usages::HidUsageKeyLeftShift],
1189                [Usages::HidUsageKeyL, Usages::HidUsageKeyLeftShift],
1190                [Usages::HidUsageKeyLeftShift],
1191                [Usages::HidUsageKeyL, Usages::HidUsageKeyLeftShift],
1192                [Usages::HidUsageKey1, Usages::HidUsageKeyLeftShift],
1193                [],
1194            ]
1195        );
1196    }
1197
1198    #[test]
1199    fn tab_and_newline() {
1200        assert_eq!(
1201            derive_key_sequence(&keymaps::US_QWERTY, "\tHello\n"),
1202            reports![
1203                [Usages::HidUsageKeyTab],
1204                [Usages::HidUsageKeyLeftShift],
1205                [Usages::HidUsageKeyH, Usages::HidUsageKeyLeftShift],
1206                [],
1207                [Usages::HidUsageKeyE],
1208                [Usages::HidUsageKeyL],
1209                [],
1210                [Usages::HidUsageKeyL],
1211                [Usages::HidUsageKeyO],
1212                [Usages::HidUsageKeyEnter],
1213                [],
1214            ]
1215        );
1216    }
1217
1218    #[fuchsia::test]
1219    async fn test_schedule_power_button_press() {
1220        use fidl_fuchsia_input_report::{ConsumerControlButton, InputReport};
1221        use fidl_fuchsia_time_alarms as fta;
1222        use fidl_fuchsia_ui_test_input::MediaButtonsDeviceMarker;
1223        use futures::{FutureExt, StreamExt};
1224
1225        let (report_sender, mut report_receiver) =
1226            futures::channel::mpsc::unbounded::<InputReport>();
1227        let input_device = crate::input_device::InputDevice::new_for_test(report_sender);
1228
1229        let (proxy, stream) =
1230            fidl::endpoints::create_proxy_and_stream::<MediaButtonsDeviceMarker>();
1231
1232        let (alarm_proxy, mut alarm_stream) =
1233            fidl::endpoints::create_proxy_and_stream::<fta::WakeAlarmsMarker>();
1234        let alarm_proxy_clone = alarm_proxy.clone();
1235
1236        let handler_fut = handle_media_buttons_device_request_stream(
1237            input_device,
1238            stream,
1239            None,
1240            Some(alarm_proxy_clone),
1241        );
1242
1243        let test_fut = async {
1244            let schedule_fut = proxy.schedule_simulate_button_press(
1245                &fidl_fuchsia_ui_test_input::MediaButtonsDeviceScheduleSimulateButtonPressRequest {
1246                    #[cfg(fuchsia_api_level_at_least = "32")]
1247                    button: Some(fidl_fuchsia_input::ConsumerControlButton::Power),
1248                    #[cfg(not(fuchsia_api_level_at_least = "32"))]
1249                    button: Some(ConsumerControlButton::Power),
1250                    delay: Some(zx::Duration::<zx::BootTimeline>::from_millis(750).into_nanos()), // 0.75s
1251                    ..Default::default()
1252                },
1253            );
1254
1255            schedule_fut.await.expect("failed to schedule");
1256
1257            // Mock WakeAlarms.SetAndWait.
1258            if let Some(Ok(fta::WakeAlarmsRequest::SetAndWait { responder, .. })) =
1259                alarm_stream.next().await
1260            {
1261                let (lease, _peer) = zx::EventPair::create();
1262                responder.send(Ok(lease)).expect("failed to respond");
1263            }
1264
1265            // Receive press report
1266            let press_report: InputReport = report_receiver.next().await.expect("no press report");
1267            assert!(press_report.consumer_control.is_some());
1268            let cc = press_report.consumer_control.unwrap();
1269            assert_eq!(cc.pressed_buttons, Some(vec![ConsumerControlButton::Power]));
1270            assert!(press_report.wake_lease.is_some());
1271
1272            // Receive release report
1273            let release_report: InputReport =
1274                report_receiver.next().await.expect("no release report");
1275            let cc = release_report.consumer_control.unwrap();
1276            assert_eq!(cc.pressed_buttons, Some(vec![]));
1277            assert!(release_report.wake_lease.is_some());
1278        };
1279
1280        futures::select! {
1281            _ = handler_fut.fuse() => panic!("handler exited early"),
1282            _ = test_fut.fuse() => (),
1283        }
1284    }
1285}