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