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