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::toggle_events::ClientConnectionsToggleEvent;
22pub use util::sender::TelemetrySender;
23#[cfg(test)]
24mod testing;
25
26const PERSISTENCE_SERVICE_PATH: &str = "/svc/fuchsia.diagnostics.persist.DataPersistence-wlan";
28
29#[derive(Debug)]
30pub enum TelemetryEvent {
31 ConnectResult {
32 result: fidl_ieee80211::StatusCode,
33 bss: Box<BssDescription>,
34 },
35 Disconnect {
36 info: DisconnectInfo,
37 },
38 ClientConnectionsToggle {
41 event: ClientConnectionsToggleEvent,
42 },
43 ClientIfaceCreated {
44 iface_id: u16,
45 },
46 ClientIfaceDestroyed {
47 iface_id: u16,
48 },
49 IfacePowerLevelChanged {
50 iface_power_level: IfacePowerLevel,
51 iface_id: u16,
52 },
53 SuspendImminent,
55 UnclearPowerDemand(UnclearPowerDemand),
57}
58
59pub async fn setup_cobalt_proxy(
61) -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, anyhow::Error> {
62 let cobalt_1dot1_svc = fuchsia_component::client::connect_to_protocol::<
63 fidl_fuchsia_metrics::MetricEventLoggerFactoryMarker,
64 >()
65 .context("failed to connect to metrics service")?;
66
67 let (cobalt_1dot1_proxy, cobalt_1dot1_server) =
68 fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>();
69
70 let project_spec = fidl_fuchsia_metrics::ProjectSpec {
71 customer_id: Some(metrics::CUSTOMER_ID),
72 project_id: Some(metrics::PROJECT_ID),
73 ..Default::default()
74 };
75
76 match cobalt_1dot1_svc.create_metric_event_logger(&project_spec, cobalt_1dot1_server).await {
77 Ok(_) => Ok(cobalt_1dot1_proxy),
78 Err(err) => Err(format_err!("failed to create metrics event logger: {:?}", err)),
79 }
80}
81
82pub fn setup_disconnected_cobalt_proxy(
85) -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, anyhow::Error> {
86 Ok(fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>().0)
88}
89
90pub fn setup_persistence_req_sender(
91) -> Result<(auto_persist::PersistenceReqSender, impl Future<Output = ()>), anyhow::Error> {
92 fuchsia_component::client::connect_to_protocol_at_path::<
93 fidl_fuchsia_diagnostics_persist::DataPersistenceMarker,
94 >(PERSISTENCE_SERVICE_PATH)
95 .map(auto_persist::create_persistence_req_sender)
96}
97
98pub fn setup_disconnected_persistence_req_sender() -> auto_persist::PersistenceReqSender {
101 let (sender, _receiver) = mpsc::channel::<String>(1);
102 sender
107}
108
109const TELEMETRY_QUERY_INTERVAL: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(10);
111
112pub fn serve_telemetry(
113 cobalt_1dot1_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
114 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
115 inspect_node: InspectNode,
116 inspect_path: &str,
117 persistence_req_sender: auto_persist::PersistenceReqSender,
118) -> (TelemetrySender, impl Future<Output = Result<(), Error>>) {
119 let (sender, mut receiver) =
120 mpsc::channel::<TelemetryEvent>(util::sender::TELEMETRY_EVENT_BUFFER_SIZE);
121 let sender = TelemetrySender::new(sender);
122
123 const METADATA_NODE_NAME: &str = "metadata";
125 let inspect_metadata_node = inspect_node.create_child(METADATA_NODE_NAME);
126 let inspect_time_series_node = inspect_node.create_child("time_series");
127 let driver_specific_time_series_node = inspect_time_series_node.create_child("driver_specific");
128 let driver_counters_time_series_node =
129 driver_specific_time_series_node.create_child("counters");
130 let driver_gauges_time_series_node = driver_specific_time_series_node.create_child("gauges");
131
132 let (time_matrix_client, time_series_fut) =
133 serve_time_matrix_inspection(inspect_time_series_node);
134 let (driver_counters_time_series_client, driver_counters_time_series_fut) =
135 serve_time_matrix_inspection(driver_counters_time_series_node);
136 let (driver_gauges_time_series_client, driver_gauges_time_series_fut) =
137 serve_time_matrix_inspection(driver_gauges_time_series_node);
138
139 let connect_disconnect = processors::connect_disconnect::ConnectDisconnectLogger::new(
141 cobalt_1dot1_proxy.clone(),
142 &inspect_node,
143 &inspect_metadata_node,
144 &format!("{inspect_path}/{METADATA_NODE_NAME}"),
145 persistence_req_sender,
146 &time_matrix_client,
147 );
148 let power_logger =
149 processors::power::PowerLogger::new(cobalt_1dot1_proxy.clone(), &inspect_node);
150 let mut toggle_logger =
151 processors::toggle_events::ToggleLogger::new(cobalt_1dot1_proxy, &inspect_node);
152
153 let client_iface_counters_logger =
154 processors::client_iface_counters::ClientIfaceCountersLogger::new(
155 monitor_svc_proxy,
156 &time_matrix_client,
157 driver_counters_time_series_client,
158 driver_gauges_time_series_client,
159 );
160
161 let fut = async move {
162 let _inspect_node = inspect_node;
164 let _inspect_metadata_node = inspect_metadata_node;
165 let _driver_specific_time_series_node = driver_specific_time_series_node;
166
167 let mut telemetry_interval = fasync::Interval::new(TELEMETRY_QUERY_INTERVAL);
168 loop {
169 select! {
170 event = receiver.next() => {
171 let Some(event) = event else {
172 error!("Telemetry event stream unexpectedly terminated.");
173 return Err(format_err!("Telemetry event stream unexpectedly terminated."));
174 };
175 use TelemetryEvent::*;
176 match event {
177 ConnectResult { result, bss } => {
178 connect_disconnect.log_connect_attempt(result, &bss).await;
179 }
180 Disconnect { info } => {
181 connect_disconnect.log_disconnect(&info).await;
182 power_logger.handle_iface_disconnect(info.iface_id).await;
183 }
184 ClientConnectionsToggle { event } => {
185 toggle_logger.log_toggle_event(event).await;
186 }
187 ClientIfaceCreated { iface_id } => {
188 client_iface_counters_logger.handle_iface_created(iface_id).await;
189 }
190 ClientIfaceDestroyed { iface_id } => {
191 client_iface_counters_logger.handle_iface_destroyed(iface_id).await;
192 power_logger.handle_iface_destroyed(iface_id).await;
193 }
194 IfacePowerLevelChanged { iface_power_level, iface_id } => {
195 power_logger.log_iface_power_event(iface_power_level, iface_id).await;
196 }
197 SuspendImminent => {
200 power_logger.handle_suspend_imminent().await;
201 }
202 UnclearPowerDemand(demand) => {
203 power_logger.handle_unclear_power_demand(demand).await;
204 }
205
206 }
207 }
208 _ = telemetry_interval.next() => {
209 client_iface_counters_logger.handle_periodic_telemetry(connect_disconnect.is_connected()).await;
210 }
211 }
212 }
213 };
214 let fut = future::try_join4(
215 fut,
216 time_series_fut,
217 driver_counters_time_series_fut,
218 driver_gauges_time_series_fut,
219 )
220 .map_ok(|((), (), (), ())| ());
221 (sender, fut)
222}