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