Skip to main content

input_pipeline/light_sensor/
light_sensor_binding.rs

1// Copyright 2022 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 super::types::Rgbc;
6use crate::input_device::{
7    self, Handled, InputDeviceBinding, InputDeviceDescriptor, InputDeviceStatus, InputEvent,
8};
9use crate::metrics;
10use anyhow::{Error, format_err};
11use async_trait::async_trait;
12use derivative::Derivative;
13use fidl_next_fuchsia_input_report::{InputReport, SensorType};
14use fuchsia_inspect::health::Reporter;
15use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
16use metrics_registry::*;
17
18#[derive(Derivative, Clone)]
19#[derivative(Debug)]
20pub struct LightSensorEvent {
21    #[derivative(Debug = "ignore")]
22    pub(crate) device_proxy:
23        fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, crate::Transport>,
24    pub(crate) rgbc: Rgbc<u16>,
25}
26
27impl PartialEq for LightSensorEvent {
28    fn eq(&self, other: &Self) -> bool {
29        self.rgbc == other.rgbc
30    }
31}
32
33impl Eq for LightSensorEvent {}
34
35impl LightSensorEvent {
36    pub fn record_inspect(&self, node: &fuchsia_inspect::Node) {
37        node.record_uint("red", u64::from(self.rgbc.red));
38        node.record_uint("green", u64::from(self.rgbc.green));
39        node.record_uint("blue", u64::from(self.rgbc.blue));
40        node.record_uint("clear", u64::from(self.rgbc.clear));
41    }
42}
43
44/// A [`LightSensorBinding`] represents a connection to a light sensor input device.
45///
46/// TODO more details
47pub(crate) struct LightSensorBinding {
48    /// The channel to stream InputEvents to.
49    event_sender: UnboundedSender<Vec<InputEvent>>,
50
51    /// Holds information about this device.
52    device_descriptor: LightSensorDeviceDescriptor,
53}
54
55#[derive(Copy, Clone, Debug, Eq, PartialEq)]
56pub struct LightSensorDeviceDescriptor {
57    /// The vendor id of the connected light sensor input device.
58    pub(crate) vendor_id: u32,
59
60    /// The product id of the connected light sensor input device.
61    pub(crate) product_id: u32,
62
63    /// The device id of the connected light sensor input device.
64    pub(crate) device_id: u32,
65
66    /// Layout of the color channels in the sensor report.
67    pub(crate) sensor_layout: Rgbc<usize>,
68}
69
70#[async_trait]
71impl InputDeviceBinding for LightSensorBinding {
72    fn input_event_sender(&self) -> UnboundedSender<Vec<InputEvent>> {
73        self.event_sender.clone()
74    }
75
76    fn get_device_descriptor(&self) -> InputDeviceDescriptor {
77        InputDeviceDescriptor::LightSensor(self.device_descriptor.clone())
78    }
79}
80
81impl LightSensorBinding {
82    /// Creates a new [`InputDeviceBinding`] from the `device_proxy`.
83    ///
84    /// The binding will start listening for input reports immediately and send new InputEvents
85    /// to the device binding owner over `input_event_sender`.
86    ///
87    /// # Parameters
88    /// - `device_proxy`: The proxy to bind the new [`InputDeviceBinding`] to.
89    /// - `device_id`: The unique identifier of this device.
90    /// - `input_event_sender`: The channel to send new InputEvents to.
91    /// - `device_node`: The inspect node for this device binding
92    /// - `metrics_logger`: The metrics logger.
93    ///
94    /// # Errors
95    /// If there was an error binding to the proxy.
96    pub(crate) async fn new(
97        device_proxy: fidl_next::Client<
98            fidl_next_fuchsia_input_report::InputDevice,
99            crate::Transport,
100        >,
101        device_id: u32,
102        input_event_sender: UnboundedSender<Vec<InputEvent>>,
103        device_node: fuchsia_inspect::Node,
104        feature_flags: input_device::InputPipelineFeatureFlags,
105        metrics_logger: metrics::MetricsLogger,
106    ) -> Result<Self, Error> {
107        let (device_binding, mut inspect_status) = Self::bind_device(
108            &device_proxy,
109            device_id,
110            input_event_sender,
111            device_node,
112            metrics_logger.clone(),
113        )
114        .await?;
115        inspect_status.health_node.set_ok();
116        input_device::initialize_report_stream(
117            device_proxy.clone(),
118            device_binding.get_device_descriptor(),
119            device_binding.input_event_sender(),
120            inspect_status,
121            metrics_logger,
122            feature_flags,
123            move |report,
124                  previous_report,
125                  device_descriptor,
126                  input_event_sender,
127                  inspect_status,
128                  metrics_logger,
129                  _feature_flags| {
130                Self::process_reports(
131                    report,
132                    previous_report,
133                    device_descriptor,
134                    input_event_sender,
135                    device_proxy.clone(),
136                    inspect_status,
137                    metrics_logger,
138                )
139            },
140        );
141
142        Ok(device_binding)
143    }
144
145    /// Binds the provided input device to a new instance of `LightSensorBinding`.
146    ///
147    /// # Parameters
148    /// - `device`: The device to use to initialize the binding.
149    /// - `device_id`: The device ID being bound.
150    /// - `input_event_sender`: The channel to send new InputEvents to.
151    /// - `device_node`: The inspect node for this device binding
152    ///
153    /// # Errors
154    /// If the device descriptor could not be retrieved, or the descriptor could not be parsed
155    /// correctly.
156    async fn bind_device(
157        device: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, crate::Transport>,
158        device_id: u32,
159        input_event_sender: UnboundedSender<Vec<InputEvent>>,
160        device_node: fuchsia_inspect::Node,
161        metrics_logger: metrics::MetricsLogger,
162    ) -> Result<(Self, InputDeviceStatus), Error> {
163        let mut input_device_status = InputDeviceStatus::new(device_node);
164        let descriptor = match device.get_descriptor().await {
165            Ok(descriptor) => descriptor.descriptor,
166            Err(_) => {
167                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
168                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
169            }
170        };
171        let device_info = descriptor.device_information.ok_or_else(|| {
172            input_device_status.health_node.set_unhealthy("Empty device_info in descriptor.");
173            // Logging in addition to returning an error, as in some test
174            // setups the error may never be displayed to the user.
175            metrics_logger.log_error(
176                InputPipelineErrorMetricDimensionEvent::LightEmptyDeviceInfo,
177                std::format!("DRIVER BUG: empty device_info for device_id: {}", device_id),
178            );
179            format_err!("empty device info for device_id: {}", device_id)
180        })?;
181        match descriptor.sensor {
182            Some(fidl_next_fuchsia_input_report::SensorDescriptor {
183                input: Some(input_descriptors),
184                ..
185            }) => {
186                let sensor_layout = input_descriptors
187                    .into_iter()
188                    .filter_map(|input_descriptor| {
189                        input_descriptor.values.and_then(|values| {
190                            let mut red_value = None;
191                            let mut green_value = None;
192                            let mut blue_value = None;
193                            let mut clear_value = None;
194                            for (i, value) in values.iter().enumerate() {
195                                let old = match value.type_ {
196                                    SensorType::LightRed => {
197                                        std::mem::replace(&mut red_value, Some(i))
198                                    }
199                                    SensorType::LightGreen => {
200                                        std::mem::replace(&mut green_value, Some(i))
201                                    }
202                                    SensorType::LightBlue => {
203                                        std::mem::replace(&mut blue_value, Some(i))
204                                    }
205                                    SensorType::LightIlluminance => {
206                                        std::mem::replace(&mut clear_value, Some(i))
207                                    }
208                                    type_ => {
209                                        log::warn!(
210                                            "unexpected sensor type {type_:?} found on light \
211                                                sensor device"
212                                        );
213                                        None
214                                    }
215                                };
216                                if old.is_some() {
217                                    log::warn!(
218                                        "existing index for light sensor {:?} replaced",
219                                        value.type_
220                                    );
221                                }
222                            }
223
224                            red_value.and_then(|red| {
225                                green_value.and_then(|green| {
226                                    blue_value.and_then(|blue| {
227                                        clear_value.map(|clear| Rgbc { red, green, blue, clear })
228                                    })
229                                })
230                            })
231                        })
232                    })
233                    .next()
234                    .ok_or_else(|| {
235                        input_device_status.health_node.set_unhealthy("Missing light sensor data.");
236                        format_err!("missing sensor data in device")
237                    })?;
238                Ok((
239                    LightSensorBinding {
240                        event_sender: input_event_sender,
241                        device_descriptor: LightSensorDeviceDescriptor {
242                            vendor_id: device_info.vendor_id.unwrap_or_default(),
243                            product_id: device_info.product_id.unwrap_or_default(),
244                            device_id,
245                            sensor_layout,
246                        },
247                    },
248                    input_device_status,
249                ))
250            }
251            device_descriptor => {
252                input_device_status
253                    .health_node
254                    .set_unhealthy("Light Sensor Device Descriptor failed to parse.");
255                Err(format_err!(
256                    "Light Sensor Device Descriptor failed to parse: \n {:?}",
257                    device_descriptor
258                ))
259            }
260        }
261    }
262
263    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
264    ///
265    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
266    ///
267    /// # Parameters
268    /// `reports`: The incoming [`InputReport`].
269    /// `previous_report`: The previous [`InputReport`] seen for the same device.
270    /// `device_descriptor`: The descriptor for the input device generating the input reports.
271    /// `input_event_sender`: The sender for the device binding's input event stream.
272    ///
273    /// # Returns
274    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
275    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
276    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
277    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
278    /// binding does not generate InputEvents asynchronously, this will be `None`.
279    ///
280    /// The returned [`InputReport`] is guaranteed to have no `wake_lease`.
281    fn process_reports(
282        reports: Vec<InputReport>,
283        mut previous_report: Option<InputReport>,
284        device_descriptor: &input_device::InputDeviceDescriptor,
285        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
286        device_proxy: fidl_next::Client<
287            fidl_next_fuchsia_input_report::InputDevice,
288            crate::Transport,
289        >,
290        inspect_status: &InputDeviceStatus,
291        metrics_logger: &metrics::MetricsLogger,
292    ) -> (Option<InputReport>, Option<UnboundedReceiver<InputEvent>>) {
293        fuchsia_trace::duration!("input", "light-sensor-binding-process-reports", "num_reports" => reports.len());
294        for report in reports {
295            previous_report = Self::process_report(
296                report,
297                previous_report,
298                device_descriptor,
299                input_event_sender,
300                device_proxy.clone(),
301                inspect_status,
302                metrics_logger,
303            );
304        }
305        (previous_report, None)
306    }
307
308    fn process_report(
309        report: InputReport,
310        previous_report: Option<InputReport>,
311        device_descriptor: &input_device::InputDeviceDescriptor,
312        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
313        device_proxy: fidl_next::Client<
314            fidl_next_fuchsia_input_report::InputDevice,
315            crate::Transport,
316        >,
317        inspect_status: &InputDeviceStatus,
318        metrics_logger: &metrics::MetricsLogger,
319    ) -> Option<InputReport> {
320        if let Some(trace_id) = report.trace_id {
321            fuchsia_trace::flow_end!("input", "input_report", trace_id.into());
322        }
323
324        inspect_status.count_received_report(&report);
325        let light_sensor_descriptor =
326            if let input_device::InputDeviceDescriptor::LightSensor(light_sensor_descriptor) =
327                device_descriptor
328            {
329                light_sensor_descriptor
330            } else {
331                unreachable!()
332            };
333
334        // Input devices can have multiple types so ensure `report` is a KeyboardInputReport.
335        let sensor = match &report.sensor {
336            None => {
337                inspect_status.count_filtered_report();
338                return previous_report;
339            }
340            Some(sensor) => sensor,
341        };
342
343        let values = match &sensor.values {
344            None => {
345                inspect_status.count_filtered_report();
346                return None;
347            }
348            Some(values) => values,
349        };
350
351        let event = input_device::InputEvent {
352            device_event: input_device::InputDeviceEvent::LightSensor(LightSensorEvent {
353                device_proxy,
354                rgbc: Rgbc {
355                    red: values[light_sensor_descriptor.sensor_layout.red] as u16,
356                    green: values[light_sensor_descriptor.sensor_layout.green] as u16,
357                    blue: values[light_sensor_descriptor.sensor_layout.blue] as u16,
358                    clear: values[light_sensor_descriptor.sensor_layout.clear] as u16,
359                },
360            }),
361            device_descriptor: device_descriptor.clone(),
362            event_time: zx::MonotonicInstant::get(),
363            handled: Handled::No,
364            trace_id: None,
365        };
366
367        if let Err(e) = input_event_sender.unbounded_send(vec![event.clone()]) {
368            metrics_logger.log_error(
369                InputPipelineErrorMetricDimensionEvent::LightFailedToSendEvent,
370                std::format!("Failed to send LightSensorEvent with error: {e:?}"),
371            );
372        } else {
373            inspect_status.count_generated_event(event);
374        }
375
376        Some(report)
377    }
378}