wlan_telemetry/
lib.rs
1use anyhow::{format_err, Context as _, Error};
5use fuchsia_inspect::Node as InspectNode;
6use futures::channel::mpsc;
7use futures::{future, select, Future, StreamExt, TryFutureExt};
8use log::error;
9use std::boxed::Box;
10use windowed_stats::experimental::serve::serve_time_matrix_inspection;
11use wlan_common::bss::BssDescription;
12use {
13 fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fuchsia_async as fasync,
14 fuchsia_inspect_auto_persist as auto_persist, wlan_legacy_metrics_registry as metrics,
15};
16
17mod processors;
18pub(crate) mod util;
19pub use crate::processors::connect_disconnect::DisconnectInfo;
20pub use crate::processors::power::{IfacePowerLevel, UnclearPowerDemand};
21pub use crate::processors::scan::ScanResult;
22pub use crate::processors::toggle_events::ClientConnectionsToggleEvent;
23pub use util::sender::TelemetrySender;
24#[cfg(test)]
25mod testing;
26
27#[derive(Debug)]
28pub enum TelemetryEvent {
29 ConnectResult {
30 result: fidl_ieee80211::StatusCode,
31 bss: Box<BssDescription>,
32 },
33 Disconnect {
34 info: DisconnectInfo,
35 },
36 ClientConnectionsToggle {
39 event: ClientConnectionsToggleEvent,
40 },
41 ClientIfaceCreated {
42 iface_id: u16,
43 },
44 ClientIfaceDestroyed {
45 iface_id: u16,
46 },
47 ScanStart,
48 ScanResult {
49 result: ScanResult,
50 },
51 IfacePowerLevelChanged {
52 iface_power_level: IfacePowerLevel,
53 iface_id: u16,
54 },
55 SuspendImminent,
57 UnclearPowerDemand(UnclearPowerDemand),
59}
60
61pub async fn setup_cobalt_proxy(
63) -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, anyhow::Error> {
64 let cobalt_1dot1_svc = fuchsia_component::client::connect_to_protocol::<
65 fidl_fuchsia_metrics::MetricEventLoggerFactoryMarker,
66 >()
67 .context("failed to connect to metrics service")?;
68
69 let (cobalt_1dot1_proxy, cobalt_1dot1_server) =
70 fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>();
71
72 let project_spec = fidl_fuchsia_metrics::ProjectSpec {
73 customer_id: Some(metrics::CUSTOMER_ID),
74 project_id: Some(metrics::PROJECT_ID),
75 ..Default::default()
76 };
77
78 match cobalt_1dot1_svc.create_metric_event_logger(&project_spec, cobalt_1dot1_server).await {
79 Ok(_) => Ok(cobalt_1dot1_proxy),
80 Err(err) => Err(format_err!("failed to create metrics event logger: {:?}", err)),
81 }
82}
83
84pub fn setup_disconnected_cobalt_proxy(
87) -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, anyhow::Error> {
88 Ok(fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>().0)
90}
91
92pub fn setup_persistence_req_sender(
93) -> Result<(auto_persist::PersistenceReqSender, impl Future<Output = ()>), anyhow::Error> {
94 fuchsia_component::client::connect_to_protocol::<
95 fidl_fuchsia_diagnostics_persist::DataPersistenceMarker,
96 >()
97 .map(auto_persist::create_persistence_req_sender)
98}
99
100pub fn setup_disconnected_persistence_req_sender() -> auto_persist::PersistenceReqSender {
103 let (sender, _receiver) = mpsc::channel::<String>(1);
104 sender
109}
110
111const TELEMETRY_QUERY_INTERVAL: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(10);
113
114pub fn serve_telemetry(
115 cobalt_1dot1_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
116 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
117 inspect_node: InspectNode,
118 inspect_path: &str,
119 persistence_req_sender: auto_persist::PersistenceReqSender,
120) -> (TelemetrySender, impl Future<Output = Result<(), Error>>) {
121 let (sender, mut receiver) =
122 mpsc::channel::<TelemetryEvent>(util::sender::TELEMETRY_EVENT_BUFFER_SIZE);
123 let sender = TelemetrySender::new(sender);
124
125 const METADATA_NODE_NAME: &str = "metadata";
127 let inspect_metadata_node = inspect_node.create_child(METADATA_NODE_NAME);
128 let inspect_time_series_node = inspect_node.create_child("time_series");
129 let driver_specific_time_series_node = inspect_time_series_node.create_child("driver_specific");
130 let driver_counters_time_series_node =
131 driver_specific_time_series_node.create_child("counters");
132 let driver_gauges_time_series_node = driver_specific_time_series_node.create_child("gauges");
133
134 let (time_matrix_client, time_series_fut) =
135 serve_time_matrix_inspection(inspect_time_series_node);
136 let (driver_counters_time_series_client, driver_counters_time_series_fut) =
137 serve_time_matrix_inspection(driver_counters_time_series_node);
138 let (driver_gauges_time_series_client, driver_gauges_time_series_fut) =
139 serve_time_matrix_inspection(driver_gauges_time_series_node);
140
141 let connect_disconnect = processors::connect_disconnect::ConnectDisconnectLogger::new(
143 cobalt_1dot1_proxy.clone(),
144 &inspect_node,
145 &inspect_metadata_node,
146 &format!("{inspect_path}/{METADATA_NODE_NAME}"),
147 persistence_req_sender,
148 &time_matrix_client,
149 );
150 let power_logger =
151 processors::power::PowerLogger::new(cobalt_1dot1_proxy.clone(), &inspect_node);
152 let mut scan_logger = processors::scan::ScanLogger::new(cobalt_1dot1_proxy.clone());
153 let mut toggle_logger =
154 processors::toggle_events::ToggleLogger::new(cobalt_1dot1_proxy.clone(), &inspect_node);
155
156 let client_iface_counters_logger =
157 processors::client_iface_counters::ClientIfaceCountersLogger::new(
158 cobalt_1dot1_proxy,
159 monitor_svc_proxy,
160 &time_matrix_client,
161 driver_counters_time_series_client,
162 driver_gauges_time_series_client,
163 );
164
165 let fut = async move {
166 let _inspect_node = inspect_node;
168 let _inspect_metadata_node = inspect_metadata_node;
169 let _driver_specific_time_series_node = driver_specific_time_series_node;
170
171 let mut telemetry_interval = fasync::Interval::new(TELEMETRY_QUERY_INTERVAL);
172 loop {
173 select! {
174 event = receiver.next() => {
175 let Some(event) = event else {
176 error!("Telemetry event stream unexpectedly terminated.");
177 return Err(format_err!("Telemetry event stream unexpectedly terminated."));
178 };
179 use TelemetryEvent::*;
180 match event {
181 ConnectResult { result, bss } => {
182 connect_disconnect.handle_connect_attempt(result, &bss).await;
183 }
184 Disconnect { info } => {
185 connect_disconnect.log_disconnect(&info).await;
186 power_logger.handle_iface_disconnect(info.iface_id).await;
187 }
188 ClientConnectionsToggle { event } => {
189 toggle_logger.log_toggle_event(event).await;
190 }
191 ClientIfaceCreated { iface_id } => {
192 client_iface_counters_logger.handle_iface_created(iface_id).await;
193 }
194 ClientIfaceDestroyed { iface_id } => {
195 client_iface_counters_logger.handle_iface_destroyed(iface_id).await;
196 power_logger.handle_iface_destroyed(iface_id).await;
197 }
198 ScanStart => {
199 scan_logger.handle_scan_start().await;
200 }
201 ScanResult { result } => {
202 scan_logger.handle_scan_result(result).await;
203 }
204 IfacePowerLevelChanged { iface_power_level, iface_id } => {
205 power_logger.log_iface_power_event(iface_power_level, iface_id).await;
206 }
207 SuspendImminent => {
210 power_logger.handle_suspend_imminent().await;
211 }
212 UnclearPowerDemand(demand) => {
213 power_logger.handle_unclear_power_demand(demand).await;
214 }
215
216 }
217 }
218 _ = telemetry_interval.next() => {
219 connect_disconnect.handle_periodic_telemetry().await;
220 client_iface_counters_logger.handle_periodic_telemetry().await;
221 }
222 }
223 }
224 };
225 let fut = future::try_join4(
226 fut,
227 time_series_fut,
228 driver_counters_time_series_fut,
229 driver_gauges_time_series_fut,
230 )
231 .map_ok(|((), (), (), ())| ());
232 (sender, fut)
233}