1use anyhow::{Context as _, Error, format_err};
6use async_trait::async_trait;
7use fidl_fuchsia_input_report::{
8 DeviceDescriptor, InputReportsReaderV2Event, InputReportsReaderV2EventStream,
9 InputReportsReaderV2Marker, InputReportsReaderV2Proxy, SensorInputDescriptor, SensorType,
10 ServiceMarker,
11};
12use fuchsia_component::client::Service;
13use futures::lock::Mutex;
14
15#[derive(Debug)]
16pub struct AmbientLightInputRpt {
17 pub illuminance: f32,
18 pub red: f32,
19 pub green: f32,
20 pub blue: f32,
21}
22
23struct AmbientLightComponent {
24 pub report_index: usize,
25 pub exponent: i32,
26 pub report_id: u8, }
28
29struct AmbientLightInputReportReaderProxy {
30 pub proxy: InputReportsReaderV2Proxy,
31 pub event_stream: Mutex<InputReportsReaderV2EventStream>,
32
33 pub illuminance: Option<AmbientLightComponent>,
34 pub red: Option<AmbientLightComponent>,
35 pub green: Option<AmbientLightComponent>,
36 pub blue: Option<AmbientLightComponent>,
37}
38
39use futures::StreamExt;
40
41async fn open_sensor_input_report_reader() -> Result<AmbientLightInputReportReaderProxy, Error> {
42 let mut watcher = Service::open(ServiceMarker)?
43 .watch()
44 .await
45 .context("Failed to watch for sensor service instances")?;
46
47 while let Some(instance_result) = watcher.next().await {
48 let instance = instance_result?;
49 let device = instance.connect_to_input_device()?;
50
51 fn get_sensor_input(
52 descriptor: &DeviceDescriptor,
53 ) -> Result<&Vec<SensorInputDescriptor>, Error> {
54 let sensor = descriptor.sensor.as_ref().context("device has no sensor")?;
55 let input_desc = sensor.input.as_ref().context("sensor has no input descriptor")?;
56 Ok(input_desc)
57 }
58
59 if let Ok(descriptor) = device.get_descriptor().await {
60 match get_sensor_input(&descriptor) {
61 Ok(input_desc) => {
62 let mut illuminance = None;
63 let mut red = None;
64 let mut green = None;
65 let mut blue = None;
66
67 for input in input_desc {
68 let axes =
69 input.values.as_ref().context("SensorInputDescriptor has no values")?;
70 for (i, val) in axes.iter().enumerate() {
71 let component = AmbientLightComponent {
72 report_index: i,
73 exponent: val.axis.unit.exponent,
74 report_id: input.report_id.unwrap_or(0),
75 };
76 match val.type_ {
77 SensorType::LightIlluminance => illuminance = Some(component),
78 SensorType::LightRed => red = Some(component),
79 SensorType::LightGreen => green = Some(component),
80 SensorType::LightBlue => blue = Some(component),
81 _ => {}
82 }
83 }
84 }
85
86 if illuminance.is_some() {
87 const MAX_UNACKNOWLEDGED_REPORTS_LIMIT: u16 = 120;
88 let (proxy, server_end) =
89 fidl::endpoints::create_proxy::<InputReportsReaderV2Marker>();
90 if let Ok(_) = device
91 .get_input_reports_reader_v2(
92 server_end,
93 MAX_UNACKNOWLEDGED_REPORTS_LIMIT,
94 )
95 .await
96 {
97 let event_stream = proxy.take_event_stream();
98 return Ok(AmbientLightInputReportReaderProxy {
99 proxy,
100 event_stream: Mutex::new(event_stream),
101 illuminance,
102 red,
103 blue,
104 green,
105 });
106 }
107 }
108 }
109 Err(e) => {
110 log::info!("Skip device {:?}: {}", instance.instance_name(), e);
111 }
112 };
113 }
114 }
115 Err(format_err!("no sensor found"))
116}
117
118async fn read_sensor_input_report(
120 device: &AmbientLightInputReportReaderProxy,
121) -> Result<Option<AmbientLightInputRpt>, Error> {
122 let mut event_stream = device.event_stream.lock().await;
123 match event_stream.next().await {
124 Some(Ok(InputReportsReaderV2Event::OnInputReports { reports, last_report_stamp })) => {
125 if let Err(e) = device.proxy.acknowledge_reports(last_report_stamp) {
126 log::warn!("Failed to acknowledge reports: {e}");
127 }
128 for report in reports {
129 if report.report_id.unwrap_or(0) != device.illuminance.as_ref().unwrap().report_id {
130 continue;
131 }
132
133 let sensor =
134 report.sensor.context("sensor required in InputReport for sensor device")?;
135 let values = sensor.values.context("values required in SensorInputReport")?;
136 let f = |component: &Option<AmbientLightComponent>| match component {
137 Some(val) => match val.exponent {
138 0 => values[val.report_index] as f32,
139 _ => values[val.report_index] as f32 * f32::powf(10.0, val.exponent as f32),
140 },
141 None => 0.0,
142 };
143
144 let illuminance = f(&device.illuminance);
145 let red = f(&device.red);
146 let green = f(&device.green);
147 let blue = f(&device.blue);
148
149 return Ok(Some(AmbientLightInputRpt { illuminance, red, blue, green }));
150 }
151 Ok(None)
152 }
153 Some(Ok(InputReportsReaderV2Event::_UnknownEvent { ordinal, .. })) => {
154 Err(format_err!("Unknown event received: {ordinal}"))
155 }
156 Some(Err(e)) => Err(format_err!("Event stream error: {}", e)),
157 None => Err(format_err!("Event stream ended")),
158 }
159}
160
161fn default_report() -> Result<Option<AmbientLightInputRpt>, Error> {
163 Ok(Some(AmbientLightInputRpt { illuminance: 200.0, red: 200.0, green: 200.0, blue: 200.0 }))
164}
165
166pub struct Sensor {
167 proxy: Option<AmbientLightInputReportReaderProxy>,
168}
169
170impl Sensor {
171 pub async fn new() -> Sensor {
172 let proxy = open_sensor_input_report_reader().await;
173 match proxy {
174 Ok(proxy) => return Sensor { proxy: Some(proxy) },
175 Err(_e) => {
176 println!("No valid sensor found.");
177 return Sensor { proxy: None };
178 }
179 }
180 }
181
182 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
183 if self.proxy.is_none() {
184 default_report()
185 } else {
186 read_sensor_input_report(self.proxy.as_ref().unwrap()).await
187 }
188 }
189}
190
191#[async_trait]
192pub trait SensorControl: Send {
193 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error>;
194}
195
196#[async_trait]
197impl SensorControl for Sensor {
198 async fn read(&self) -> Result<Option<AmbientLightInputRpt>, Error> {
199 self.read().await
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[fuchsia::test]
208 async fn test_open_sensor_error() {
209 let sensor = Sensor { proxy: None };
210 if let Some(ambient_light_input_rpt) = sensor.read().await.unwrap() {
211 assert_eq!(ambient_light_input_rpt.illuminance, 200.0);
212 assert_eq!(ambient_light_input_rpt.red, 200.0);
213 assert_eq!(ambient_light_input_rpt.green, 200.0);
214 assert_eq!(ambient_light_input_rpt.blue, 200.0);
215 }
216 }
217}