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