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::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, crate::dispatcher::TaskHandle<()>), Error> {
107        let (device_descriptor, mut inspect_status) =
108            Self::bind_device(&device_proxy, device_id, device_node, metrics_logger.clone())
109                .await?;
110        inspect_status.health_node.set_ok();
111        let task = input_device::initialize_report_stream(
112            device_proxy.clone(),
113            InputDeviceDescriptor::LightSensor(device_descriptor.clone()),
114            input_event_sender.clone(),
115            inspect_status,
116            metrics_logger,
117            feature_flags,
118            move |reports,
119                  previous_state,
120                  device_descriptor,
121                  input_event_sender,
122                  inspect_status,
123                  metrics_logger,
124                  _feature_flags| {
125                Self::process_reports(
126                    reports,
127                    previous_state,
128                    device_descriptor,
129                    input_event_sender,
130                    device_proxy.clone(),
131                    inspect_status,
132                    metrics_logger,
133                )
134            },
135        );
136
137        Ok((LightSensorBinding { event_sender: input_event_sender, device_descriptor }, task))
138    }
139
140    /// Binds the provided input device to a new instance of `LightSensorBinding`.
141    ///
142    /// # Parameters
143    /// - `device`: The device to use to initialize the binding.
144    /// - `device_id`: The device ID being bound.
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: &fidl_next::Client<fidl_next_fuchsia_input_report::InputDevice, crate::Transport>,
152        device_id: u32,
153        device_node: fuchsia_inspect::Node,
154        metrics_logger: metrics::MetricsLogger,
155    ) -> Result<(LightSensorDeviceDescriptor, InputDeviceStatus), Error> {
156        let mut input_device_status = InputDeviceStatus::new(device_node);
157        let descriptor = match device.get_descriptor().await {
158            Ok(descriptor) => descriptor.descriptor,
159            Err(_) => {
160                input_device_status.health_node.set_unhealthy("Could not get device descriptor.");
161                return Err(format_err!("Could not get descriptor for device_id: {}", device_id));
162            }
163        };
164        let device_info = descriptor.device_information.ok_or_else(|| {
165            input_device_status.health_node.set_unhealthy("Empty device_info in descriptor.");
166            // Logging in addition to returning an error, as in some test
167            // setups the error may never be displayed to the user.
168            metrics_logger.log_error(
169                InputPipelineErrorMetricDimensionEvent::LightEmptyDeviceInfo,
170                std::format!("DRIVER BUG: empty device_info for device_id: {}", device_id),
171            );
172            format_err!("empty device info for device_id: {}", device_id)
173        })?;
174        match descriptor.sensor {
175            Some(fidl_next_fuchsia_input_report::SensorDescriptor {
176                input: Some(input_descriptors),
177                ..
178            }) => {
179                let sensor_layout = input_descriptors
180                    .into_iter()
181                    .filter_map(|input_descriptor| {
182                        input_descriptor.values.and_then(|values| {
183                            let mut red_value = None;
184                            let mut green_value = None;
185                            let mut blue_value = None;
186                            let mut clear_value = None;
187                            for (i, value) in values.iter().enumerate() {
188                                let old = match value.type_ {
189                                    SensorType::LightRed => {
190                                        std::mem::replace(&mut red_value, Some(i))
191                                    }
192                                    SensorType::LightGreen => {
193                                        std::mem::replace(&mut green_value, Some(i))
194                                    }
195                                    SensorType::LightBlue => {
196                                        std::mem::replace(&mut blue_value, Some(i))
197                                    }
198                                    SensorType::LightIlluminance => {
199                                        std::mem::replace(&mut clear_value, Some(i))
200                                    }
201                                    type_ => {
202                                        log::warn!(
203                                            "unexpected sensor type {type_:?} found on light \
204                                                sensor device"
205                                        );
206                                        None
207                                    }
208                                };
209                                if old.is_some() {
210                                    log::warn!(
211                                        "existing index for light sensor {:?} replaced",
212                                        value.type_
213                                    );
214                                }
215                            }
216
217                            red_value.and_then(|red| {
218                                green_value.and_then(|green| {
219                                    blue_value.and_then(|blue| {
220                                        clear_value.map(|clear| Rgbc { red, green, blue, clear })
221                                    })
222                                })
223                            })
224                        })
225                    })
226                    .next()
227                    .ok_or_else(|| {
228                        input_device_status.health_node.set_unhealthy("Missing light sensor data.");
229                        format_err!("missing sensor data in device")
230                    })?;
231                Ok((
232                    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                    input_device_status,
239                ))
240            }
241            device_descriptor => {
242                input_device_status
243                    .health_node
244                    .set_unhealthy("Light Sensor Device Descriptor failed to parse.");
245                Err(format_err!(
246                    "Light Sensor Device Descriptor failed to parse: \n {:?}",
247                    device_descriptor
248                ))
249            }
250        }
251    }
252
253    /// Parses an [`InputReport`] into one or more [`InputEvent`]s.
254    ///
255    /// The [`InputEvent`]s are sent to the device binding owner via [`input_event_sender`].
256    ///
257    /// # Parameters
258    /// `reports`: The incoming [`InputReport`].
259    /// `previous_report`: The previous [`InputReport`] seen for the same device.
260    /// `device_descriptor`: The descriptor for the input device generating the input reports.
261    /// `input_event_sender`: The sender for the device binding's input event stream.
262    ///
263    /// # Returns
264    /// An [`InputReport`] which will be passed to the next call to [`process_reports`], as
265    /// [`previous_report`]. If `None`, the next call's [`previous_report`] will be `None`.
266    /// A [`UnboundedReceiver<InputEvent>`] which will poll asynchronously generated events to be
267    /// recorded by `inspect_status` in `input_device::initialize_report_stream()`. If device
268    /// binding does not generate InputEvents asynchronously, this will be `None`.
269    ///
270    /// The returned [`InputReport`] is guaranteed to have no `wake_lease`.
271    fn process_reports(
272        reports: &[fidl_next_fuchsia_input_report::wire::InputReport<'_>],
273        mut previous_state: Option<input_device::PreviousDeviceState>,
274        device_descriptor: &input_device::InputDeviceDescriptor,
275        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
276        device_proxy: fidl_next::Client<
277            fidl_next_fuchsia_input_report::InputDevice,
278            crate::Transport,
279        >,
280        inspect_status: &InputDeviceStatus,
281        metrics_logger: &metrics::MetricsLogger,
282    ) -> (Option<input_device::PreviousDeviceState>, Option<UnboundedReceiver<InputEvent>>) {
283        fuchsia_trace::duration!("input", "light-sensor-binding-process-reports", "num_reports" => reports.len());
284        for report in reports {
285            previous_state = Self::process_report(
286                report,
287                previous_state,
288                device_descriptor,
289                input_event_sender,
290                device_proxy.clone(),
291                inspect_status,
292                metrics_logger,
293            );
294        }
295        (previous_state, None)
296    }
297
298    fn process_report(
299        report: &fidl_next_fuchsia_input_report::wire::InputReport<'_>,
300        previous_state: Option<input_device::PreviousDeviceState>,
301        device_descriptor: &input_device::InputDeviceDescriptor,
302        input_event_sender: &mut UnboundedSender<Vec<InputEvent>>,
303        device_proxy: fidl_next::Client<
304            fidl_next_fuchsia_input_report::InputDevice,
305            crate::Transport,
306        >,
307        inspect_status: &InputDeviceStatus,
308        metrics_logger: &metrics::MetricsLogger,
309    ) -> Option<input_device::PreviousDeviceState> {
310        if let Some(trace_id) = report.trace_id() {
311            fuchsia_trace::flow_end!("input", "input_report", trace_id.0.into());
312        }
313
314        inspect_status.count_received_report_wire(report);
315        let light_sensor_descriptor =
316            if let input_device::InputDeviceDescriptor::LightSensor(light_sensor_descriptor) =
317                device_descriptor
318            {
319                light_sensor_descriptor
320            } else {
321                unreachable!()
322            };
323
324        // Input devices can have multiple types so ensure `report` is a KeyboardInputReport.
325        let sensor = match report.sensor() {
326            None => {
327                inspect_status.count_filtered_report();
328                return previous_state;
329            }
330            Some(sensor) => sensor,
331        };
332
333        let values = match sensor.values() {
334            None => {
335                inspect_status.count_filtered_report();
336                return None;
337            }
338            Some(values) => values,
339        };
340
341        let event = input_device::InputEvent {
342            device_event: input_device::InputDeviceEvent::LightSensor(LightSensorEvent {
343                device_proxy,
344                rgbc: Rgbc {
345                    red: values[light_sensor_descriptor.sensor_layout.red].0 as u16,
346                    green: values[light_sensor_descriptor.sensor_layout.green].0 as u16,
347                    blue: values[light_sensor_descriptor.sensor_layout.blue].0 as u16,
348                    clear: values[light_sensor_descriptor.sensor_layout.clear].0 as u16,
349                },
350            }),
351            device_descriptor: device_descriptor.clone(),
352            event_time: zx::MonotonicInstant::get(),
353            handled: Handled::No,
354            trace_id: None,
355        };
356
357        let events = vec![event];
358        inspect_status.count_generated_events(&events);
359
360        if let Err(e) = input_event_sender.unbounded_send(events) {
361            metrics_logger.log_error(
362                InputPipelineErrorMetricDimensionEvent::LightFailedToSendEvent,
363                std::format!("Failed to send LightSensorEvent with error: {e:?}"),
364            );
365        }
366
367        Some(input_device::PreviousDeviceState::LightSensor)
368    }
369}