Skip to main content

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