input_pipeline/
consumer_controls_binding.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::input_device::{self, Handled, InputDeviceBinding, InputDeviceStatus, InputEvent};
6use crate::metrics;
7use anyhow::{Error, format_err};
8use async_trait::async_trait;
9use fidl_fuchsia_input_report::{
10    self as fidl_input_report, ConsumerControlButton, InputDeviceProxy, InputReport,
11};
12use fuchsia_inspect::ArrayProperty;
13use fuchsia_inspect::health::Reporter;
14
15use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
16use metrics_registry::*;
17use zx::{AsHandleRef, HandleBased};
18
19/// A [`ConsumerControlsEvent`] represents an event where one or more consumer control buttons
20/// were pressed.
21///
22/// # Example
23/// The following ConsumerControlsEvents represents an event where the volume up button was pressed.
24///
25/// ```
26/// let volume_event = input_device::InputDeviceEvent::ConsumerControls(ConsumerControlsEvent::new(
27///     vec![ConsumerControlButton::VOLUME_UP],
28/// ));
29/// ```
30#[derive(Debug)]
31pub struct ConsumerControlsEvent {
32    pub pressed_buttons: Vec<ConsumerControlButton>,
33    pub wake_lease: Option<zx::EventPair>,
34}
35
36impl Clone for ConsumerControlsEvent {
37    fn clone(&self) -> Self {
38        Self {
39            pressed_buttons: self.pressed_buttons.clone(),
40            wake_lease: self.wake_lease.as_ref().map(|lease| {
41                lease
42                    .duplicate_handle(zx::Rights::SAME_RIGHTS)
43                    .expect("failed to duplicate event pair")
44            }),
45        }
46    }
47}
48
49impl PartialEq for ConsumerControlsEvent {
50    fn eq(&self, other: &Self) -> bool {
51        self.pressed_buttons == other.pressed_buttons
52            && self.wake_lease.as_ref().map(|h| h.get_koid())
53                == other.wake_lease.as_ref().map(|h| h.get_koid())
54    }
55}
56
57impl ConsumerControlsEvent {
58    /// Creates a new [`ConsumerControlsEvent`] with the relevant buttons.
59    ///
60    /// # Parameters
61    /// - `pressed_buttons`: The buttons relevant to this event.
62    pub fn new(
63        pressed_buttons: Vec<ConsumerControlButton>,
64        wake_lease: Option<zx::EventPair>,
65    ) -> Self {
66        Self { pressed_buttons, wake_lease }
67    }
68
69    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
70        let pressed_buttons_node =
71            node.create_string_array("pressed_buttons", self.pressed_buttons.len());
72        self.pressed_buttons.iter().enumerate().for_each(|(i, &ref button)| {
73            let button_name: String = match button {
74                ConsumerControlButton::VolumeUp => "volume_up".into(),
75                ConsumerControlButton::VolumeDown => "volume_down".into(),
76                ConsumerControlButton::Pause => "pause".into(),
77                ConsumerControlButton::FactoryReset => "factory_reset".into(),
78                ConsumerControlButton::MicMute => "mic_mute".into(),
79                ConsumerControlButton::Reboot => "reboot".into(),
80                ConsumerControlButton::CameraDisable => "camera_disable".into(),
81                ConsumerControlButton::Power => "power".into(),
82                ConsumerControlButton::Function => "function".into(),
83                unknown_value => {
84                    format!("unknown({:?})", unknown_value)
85                }
86            };
87            pressed_buttons_node.set(i, &button_name);
88        });
89        node.record(pressed_buttons_node);
90    }
91}
92
93/// A [`ConsumerControlsBinding`] represents a connection to a consumer controls input device with
94/// consumer controls. The buttons supported by this binding is returned by `supported_buttons()`.
95///
96/// The [`ConsumerControlsBinding`] parses and exposes consumer control descriptor properties
97/// for the device it is associated with. It also parses [`InputReport`]s
98/// from the device, and sends them to the device binding owner over `event_sender`.
99pub struct ConsumerControlsBinding {
100    /// The channel to stream InputEvents to.
101    event_sender: UnboundedSender<input_device::InputEvent>,
102
103    /// Holds information about this device.
104    device_descriptor: ConsumerControlsDeviceDescriptor,
105}
106
107#[derive(Clone, Debug, Eq, PartialEq)]
108pub struct ConsumerControlsDeviceDescriptor {
109    /// The list of buttons that this device contains.
110    pub buttons: Vec<ConsumerControlButton>,
111    /// Identifies the device originating this event.
112    pub device_id: u32,
113}
114
115#[async_trait]
116impl input_device::InputDeviceBinding for ConsumerControlsBinding {
117    fn input_event_sender(&self) -> UnboundedSender<input_device::InputEvent> {
118        self.event_sender.clone()
119    }
120
121    fn get_device_descriptor(&self) -> input_device::InputDeviceDescriptor {
122        input_device::InputDeviceDescriptor::ConsumerControls(self.device_descriptor.clone())
123    }
124}
125
126impl ConsumerControlsBinding {
127    /// Creates a new [`InputDeviceBinding`] from the `device_proxy`.
128    ///
129    /// The binding will start listening for input reports immediately and send new InputEvents
130    /// to the device binding owner over `input_event_sender`.
131    ///
132    /// # Parameters
133    /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to.
134    /// - `device_id`: The id of the connected device.
135    /// - `input_event_sender`: The channel to send new InputEvents to.
136    /// - `device_node`: The inspect node for this device binding
137    /// - `metrics_logger`: The metrics logger.
138    ///
139    /// # Errors
140    /// If there was an error binding to the proxy.
141    pub async fn new(
142        device_proxy: InputDeviceProxy,
143        device_id: u32,
144        input_event_sender: UnboundedSender<input_device::InputEvent>,
145        device_node: fuchsia_inspect::Node,
146        metrics_logger: metrics::MetricsLogger,
147    ) -> Result<Self, Error> {
148        let (device_binding, mut inspect_status) =
149            Self::bind_device(&device_proxy, device_id, input_event_sender, device_node).await?;
150        inspect_status.health_node.set_ok();
151        input_device::initialize_report_stream(
152            device_proxy,
153            device_binding.get_device_descriptor(),
154            device_binding.input_event_sender(),
155            inspect_status,
156            metrics_logger,
157            Self::process_reports,
158        );
159
160        Ok(device_binding)
161    }
162
163    /// Binds the provided input device to a new instance of `Self`.
164    ///
165    /// # Parameters
166    /// - `device`: The device to use to initialize the binding.
167    /// - `device_id`: The id of the connected device.
168    /// - `input_event_sender`: The channel to send new InputEvents to.
169    /// - `device_node`: The inspect node for this device binding
170    ///
171    /// # Errors
172    /// If the device descriptor could not be retrieved, or the descriptor could
173    /// not be parsed correctly.
174    async fn bind_device(
175        device: &InputDeviceProxy,
176        device_id: u32,
177        input_event_sender: UnboundedSender<input_device::InputEvent>,
178        device_node: fuchsia_inspect::Node,
179    ) -> Result<(Self, InputDeviceStatus), Error> {
180        let mut input_device_status = InputDeviceStatus::new(device_node);
181        let device_descriptor: fidl_input_report::DeviceDescriptor = match device
182            .get_descriptor()
183            .await
184        {
185            Ok(descriptor) => descriptor,
186            Err(_) => {
187                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
188                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
189            }
190        };
191
192        let consumer_controls_descriptor = device_descriptor.consumer_control.ok_or_else(|| {
193            input_device_status
194                .health_node
195                .set_unhealthy("DeviceDescriptor does not have a ConsumerControlDescriptor.");
196            format_err!("DeviceDescriptor does not have a ConsumerControlDescriptor")
197        })?;
198
199        let consumer_controls_input_descriptor =
200            consumer_controls_descriptor.input.ok_or_else(|| {
201                input_device_status.health_node.set_unhealthy(
202                    "ConsumerControlDescriptor does not have a ConsumerControlInputDescriptor.",
203                );
204                format_err!(
205                    "ConsumerControlDescriptor does not have a ConsumerControlInputDescriptor"
206                )
207            })?;
208
209        let device_descriptor: ConsumerControlsDeviceDescriptor =
210            ConsumerControlsDeviceDescriptor {
211                buttons: consumer_controls_input_descriptor.buttons.unwrap_or_default(),
212                device_id,
213            };
214
215        Ok((
216            ConsumerControlsBinding { event_sender: input_event_sender, device_descriptor },
217            input_device_status,
218        ))
219    }
220
221    /// Parses an [`InputReport`] into one or more [`InputEvent`]s. Sends the [`InputEvent`]s
222    /// to the device binding owner via [`input_event_sender`].
223    ///
224    /// # Parameters
225    /// `report`: The incoming [`InputReport`].
226    /// `previous_report`: The previous [`InputReport`] seen for the same device. This can be
227    ///                    used to determine, for example, which keys are no longer present in
228    ///                    a keyboard report to generate key released events. If `None`, no
229    ///                    previous report was found.
230    /// `device_descriptor`: The descriptor for the input device generating the input reports.
231    /// `input_event_sender`: The sender for the device binding's input event stream.
232    /// `metrics_logger`: The metrics logger.
233    ///
234    ///
235    /// # Returns
236    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
237    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
238    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
239    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
240    /// binding does not generate InputEvents asynchronously, this will be `None`.
241    fn process_reports(
242        mut report: InputReport,
243        previous_report: Option<InputReport>,
244        device_descriptor: &input_device::InputDeviceDescriptor,
245        input_event_sender: &mut UnboundedSender<input_device::InputEvent>,
246        inspect_status: &InputDeviceStatus,
247        metrics_logger: &metrics::MetricsLogger,
248    ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
249        fuchsia_trace::duration!(c"input", c"consumer-controls-binding-process-report");
250        if let Some(trace_id) = report.trace_id {
251            fuchsia_trace::flow_end!(c"input", c"input_report", trace_id.into());
252        }
253
254        inspect_status.count_received_report(&report);
255        // Input devices can have multiple types so ensure `report` is a ConsumerControlInputReport.
256        let pressed_buttons: Vec<ConsumerControlButton> = match report.consumer_control {
257            Some(ref consumer_control_report) => consumer_control_report
258                .pressed_buttons
259                .as_ref()
260                .map(|buttons| buttons.iter().cloned().collect())
261                .unwrap_or_default(),
262            None => {
263                inspect_status.count_filtered_report();
264                return (previous_report, None);
265            }
266        };
267
268        let wake_lease = report.wake_lease.take();
269        let trace_id = fuchsia_trace::Id::random();
270        fuchsia_trace::flow_begin!(c"input", c"event_in_input_pipeline", trace_id);
271
272        send_consumer_controls_event(
273            pressed_buttons,
274            wake_lease,
275            device_descriptor,
276            input_event_sender,
277            inspect_status,
278            metrics_logger,
279            trace_id,
280        );
281
282        (Some(report), None)
283    }
284}
285
286/// Sends an InputEvent over `sender`.
287///
288/// # Parameters
289/// - `pressed_buttons`: The buttons relevant to the event.
290/// - `wake_lease`: The wake lease associated with the event.
291/// - `device_descriptor`: The descriptor for the input device generating the input reports.
292/// - `sender`: The stream to send the InputEvent to.
293/// - `metrics_logger`: The metrics logger.
294/// - `trace_id`: The trace_id of this button event.
295fn send_consumer_controls_event(
296    pressed_buttons: Vec<ConsumerControlButton>,
297    wake_lease: Option<zx::EventPair>,
298    device_descriptor: &input_device::InputDeviceDescriptor,
299    sender: &mut UnboundedSender<input_device::InputEvent>,
300    inspect_status: &InputDeviceStatus,
301    metrics_logger: &metrics::MetricsLogger,
302    trace_id: fuchsia_trace::Id,
303) {
304    let event = input_device::InputEvent {
305        device_event: input_device::InputDeviceEvent::ConsumerControls(ConsumerControlsEvent::new(
306            pressed_buttons,
307            wake_lease,
308        )),
309        device_descriptor: device_descriptor.clone(),
310        event_time: zx::MonotonicInstant::get(),
311        handled: Handled::No,
312        trace_id: Some(trace_id),
313    };
314
315    match sender.unbounded_send(event.clone()) {
316        Err(e) => metrics_logger.log_error(
317            InputPipelineErrorMetricDimensionEvent::ConsumerControlsSendEventFailed,
318            std::format!("Failed to send ConsumerControlsEvent with error: {:?}", e),
319        ),
320        _ => inspect_status.count_generated_event(event),
321    }
322}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327    use crate::testing_utilities;
328    use fuchsia_async as fasync;
329    use futures::StreamExt;
330
331    // Tests that an InputReport containing one consumer control button generates an InputEvent
332    // containing the same consumer control button.
333    #[fasync::run_singlethreaded(test)]
334    async fn volume_up_only() {
335        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
336        let pressed_buttons = vec![ConsumerControlButton::VolumeUp];
337        let first_report = testing_utilities::create_consumer_control_input_report(
338            pressed_buttons.clone(),
339            event_time_i64,
340        );
341        let descriptor = testing_utilities::consumer_controls_device_descriptor();
342
343        let input_reports = vec![first_report];
344        let expected_events = vec![testing_utilities::create_consumer_controls_event(
345            pressed_buttons,
346            event_time_u64,
347            &descriptor,
348        )];
349
350        assert_input_report_sequence_generates_events!(
351            input_reports: input_reports,
352            expected_events: expected_events,
353            device_descriptor: descriptor,
354            device_type: ConsumerControlsBinding,
355        );
356    }
357
358    // Tests that an InputReport containing two consumer control buttons generates an InputEvent
359    // containing both consumer control buttons.
360    #[fasync::run_singlethreaded(test)]
361    async fn volume_up_and_down() {
362        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
363        let pressed_buttons =
364            vec![ConsumerControlButton::VolumeUp, ConsumerControlButton::VolumeDown];
365        let first_report = testing_utilities::create_consumer_control_input_report(
366            pressed_buttons.clone(),
367            event_time_i64,
368        );
369        let descriptor = testing_utilities::consumer_controls_device_descriptor();
370
371        let input_reports = vec![first_report];
372        let expected_events = vec![testing_utilities::create_consumer_controls_event(
373            pressed_buttons,
374            event_time_u64,
375            &descriptor,
376        )];
377
378        assert_input_report_sequence_generates_events!(
379            input_reports: input_reports,
380            expected_events: expected_events,
381            device_descriptor: descriptor,
382            device_type: ConsumerControlsBinding,
383        );
384    }
385
386    // Tests that three InputReports containing one consumer control button generates three
387    // InputEvents containing the same consumer control button.
388    #[fasync::run_singlethreaded(test)]
389    async fn sequence_of_buttons() {
390        let (event_time_i64, event_time_u64) = testing_utilities::event_times();
391        let first_report = testing_utilities::create_consumer_control_input_report(
392            vec![ConsumerControlButton::VolumeUp],
393            event_time_i64,
394        );
395        let second_report = testing_utilities::create_consumer_control_input_report(
396            vec![ConsumerControlButton::VolumeDown],
397            event_time_i64,
398        );
399        let third_report = testing_utilities::create_consumer_control_input_report(
400            vec![ConsumerControlButton::CameraDisable],
401            event_time_i64,
402        );
403        let descriptor = testing_utilities::consumer_controls_device_descriptor();
404
405        let input_reports = vec![first_report, second_report, third_report];
406        let expected_events = vec![
407            testing_utilities::create_consumer_controls_event(
408                vec![ConsumerControlButton::VolumeUp],
409                event_time_u64,
410                &descriptor,
411            ),
412            testing_utilities::create_consumer_controls_event(
413                vec![ConsumerControlButton::VolumeDown],
414                event_time_u64,
415                &descriptor,
416            ),
417            testing_utilities::create_consumer_controls_event(
418                vec![ConsumerControlButton::CameraDisable],
419                event_time_u64,
420                &descriptor,
421            ),
422        ];
423
424        assert_input_report_sequence_generates_events!(
425            input_reports: input_reports,
426            expected_events: expected_events,
427            device_descriptor: descriptor,
428            device_type: ConsumerControlsBinding,
429        );
430    }
431}