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_power_battery as fidl_battery, fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211,
14 fuchsia_async as fasync, fuchsia_inspect_auto_persist as auto_persist,
15 wlan_legacy_metrics_registry as metrics,
16};
17
18mod processors;
19pub(crate) mod util;
20pub use crate::processors::connect_disconnect::DisconnectInfo;
21pub use crate::processors::power::{IfacePowerLevel, UnclearPowerDemand};
22pub use crate::processors::scan::ScanResult;
23pub use crate::processors::toggle_events::ClientConnectionsToggleEvent;
24pub use util::sender::TelemetrySender;
25#[cfg(test)]
26mod testing;
27
28#[derive(Debug)]
29pub enum TelemetryEvent {
30 ConnectResult {
31 result: fidl_ieee80211::StatusCode,
32 bss: Box<BssDescription>,
33 },
34 Disconnect {
35 info: DisconnectInfo,
36 },
37 ClientConnectionsToggle {
40 event: ClientConnectionsToggleEvent,
41 },
42 ClientIfaceCreated {
43 iface_id: u16,
44 },
45 ClientIfaceDestroyed {
46 iface_id: u16,
47 },
48 IfaceCreationFailure,
49 IfaceDestructionFailure,
50 ScanStart,
51 ScanResult {
52 result: ScanResult,
53 },
54 IfacePowerLevelChanged {
55 iface_power_level: IfacePowerLevel,
56 iface_id: u16,
57 },
58 SuspendImminent,
60 UnclearPowerDemand(UnclearPowerDemand),
62 BatteryChargeStatus(fidl_battery::ChargeStatus),
63 RecoveryEvent,
64 SmeTimeout,
65}
66
67pub async fn setup_cobalt_proxy(
69) -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, anyhow::Error> {
70 let cobalt_svc = fuchsia_component::client::connect_to_protocol::<
71 fidl_fuchsia_metrics::MetricEventLoggerFactoryMarker,
72 >()
73 .context("failed to connect to metrics service")?;
74
75 let (cobalt_proxy, cobalt_server) =
76 fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>();
77
78 let project_spec = fidl_fuchsia_metrics::ProjectSpec {
79 customer_id: Some(metrics::CUSTOMER_ID),
80 project_id: Some(metrics::PROJECT_ID),
81 ..Default::default()
82 };
83
84 match cobalt_svc.create_metric_event_logger(&project_spec, cobalt_server).await {
85 Ok(_) => Ok(cobalt_proxy),
86 Err(err) => Err(format_err!("failed to create metrics event logger: {:?}", err)),
87 }
88}
89
90pub fn setup_disconnected_cobalt_proxy(
93) -> Result<fidl_fuchsia_metrics::MetricEventLoggerProxy, anyhow::Error> {
94 Ok(fidl::endpoints::create_proxy::<fidl_fuchsia_metrics::MetricEventLoggerMarker>().0)
96}
97
98pub fn setup_persistence_req_sender(
99) -> Result<(auto_persist::PersistenceReqSender, impl Future<Output = ()>), anyhow::Error> {
100 fuchsia_component::client::connect_to_protocol::<
101 fidl_fuchsia_diagnostics_persist::DataPersistenceMarker,
102 >()
103 .map(auto_persist::create_persistence_req_sender)
104}
105
106pub fn setup_disconnected_persistence_req_sender() -> auto_persist::PersistenceReqSender {
109 let (sender, _receiver) = mpsc::channel::<String>(1);
110 sender
115}
116
117const TELEMETRY_QUERY_INTERVAL: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(10);
119
120pub fn serve_telemetry(
121 cobalt_proxy: fidl_fuchsia_metrics::MetricEventLoggerProxy,
122 monitor_svc_proxy: fidl_fuchsia_wlan_device_service::DeviceMonitorProxy,
123 inspect_node: InspectNode,
124 inspect_path: &str,
125 persistence_req_sender: auto_persist::PersistenceReqSender,
126) -> (TelemetrySender, impl Future<Output = Result<(), Error>>) {
127 let (sender, mut receiver) =
128 mpsc::channel::<TelemetryEvent>(util::sender::TELEMETRY_EVENT_BUFFER_SIZE);
129 let sender = TelemetrySender::new(sender);
130
131 const METADATA_NODE_NAME: &str = "metadata";
133 let inspect_metadata_node = inspect_node.create_child(METADATA_NODE_NAME);
134 let inspect_metadata_path = format!("{inspect_path}/{METADATA_NODE_NAME}");
135 let inspect_time_series_node = inspect_node.create_child("time_series");
136 let driver_specific_time_series_node = inspect_time_series_node.create_child("driver_specific");
137 let driver_counters_time_series_node =
138 driver_specific_time_series_node.create_child("counters");
139 let driver_gauges_time_series_node = driver_specific_time_series_node.create_child("gauges");
140
141 let (time_matrix_client, time_series_fut) =
142 serve_time_matrix_inspection(inspect_time_series_node);
143 let (driver_counters_time_series_client, driver_counters_time_series_fut) =
144 serve_time_matrix_inspection(driver_counters_time_series_node);
145 let (driver_gauges_time_series_client, driver_gauges_time_series_fut) =
146 serve_time_matrix_inspection(driver_gauges_time_series_node);
147
148 let connect_disconnect = processors::connect_disconnect::ConnectDisconnectLogger::new(
150 cobalt_proxy.clone(),
151 &inspect_node,
152 &inspect_metadata_node,
153 &inspect_metadata_path,
154 persistence_req_sender,
155 &time_matrix_client,
156 );
157 let iface_logger = processors::iface::IfaceLogger::new(cobalt_proxy.clone());
158 let power_logger = processors::power::PowerLogger::new(cobalt_proxy.clone(), &inspect_node);
159 let recovery_logger = processors::recovery::RecoveryLogger::new(cobalt_proxy.clone());
160 let mut scan_logger = processors::scan::ScanLogger::new(cobalt_proxy.clone());
161 let sme_timeout_logger = processors::sme_timeout::SmeTimeoutLogger::new(cobalt_proxy.clone());
162 let mut toggle_logger =
163 processors::toggle_events::ToggleLogger::new(cobalt_proxy.clone(), &inspect_node);
164
165 let client_iface_counters_logger =
166 processors::client_iface_counters::ClientIfaceCountersLogger::new(
167 cobalt_proxy,
168 monitor_svc_proxy,
169 &inspect_metadata_node,
170 &inspect_metadata_path,
171 &time_matrix_client,
172 driver_counters_time_series_client,
173 driver_gauges_time_series_client,
174 );
175
176 let fut = async move {
177 let _inspect_node = inspect_node;
179 let _inspect_metadata_node = inspect_metadata_node;
180 let _driver_specific_time_series_node = driver_specific_time_series_node;
181
182 let mut telemetry_interval = fasync::Interval::new(TELEMETRY_QUERY_INTERVAL);
183 loop {
184 select! {
185 event = receiver.next() => {
186 let Some(event) = event else {
187 error!("Telemetry event stream unexpectedly terminated.");
188 return Err(format_err!("Telemetry event stream unexpectedly terminated."));
189 };
190 use TelemetryEvent::*;
191 match event {
192 ConnectResult { result, bss } => {
193 connect_disconnect.handle_connect_attempt(result, &bss).await;
194 }
195 Disconnect { info } => {
196 connect_disconnect.log_disconnect(&info).await;
197 power_logger.handle_iface_disconnect(info.iface_id).await;
198 }
199 ClientConnectionsToggle { event } => {
200 toggle_logger.handle_toggle_event(event).await;
201 }
202 ClientIfaceCreated { iface_id } => {
203 client_iface_counters_logger.handle_iface_created(iface_id).await;
204 }
205 ClientIfaceDestroyed { iface_id } => {
206 client_iface_counters_logger.handle_iface_destroyed(iface_id).await;
207 power_logger.handle_iface_destroyed(iface_id).await;
208 }
209 IfaceCreationFailure => {
210 iface_logger.handle_iface_creation_failure().await;
211 }
212 IfaceDestructionFailure => {
213 iface_logger.handle_iface_destruction_failure().await;
214 }
215 ScanStart => {
216 scan_logger.handle_scan_start().await;
217 }
218 ScanResult { result } => {
219 scan_logger.handle_scan_result(result).await;
220 }
221 IfacePowerLevelChanged { iface_power_level, iface_id } => {
222 power_logger.log_iface_power_event(iface_power_level, iface_id).await;
223 }
224 SuspendImminent => {
227 power_logger.handle_suspend_imminent().await;
228 connect_disconnect.handle_suspend_imminent().await;
229 }
230 UnclearPowerDemand(demand) => {
231 power_logger.handle_unclear_power_demand(demand).await;
232 }
233 BatteryChargeStatus(charge_status) => {
234 scan_logger.handle_battery_charge_status(charge_status).await;
235 toggle_logger.handle_battery_charge_status(charge_status).await;
236 }
237 RecoveryEvent => {
238 recovery_logger.handle_recovery_event().await;
239 }
240 SmeTimeout => {
241 sme_timeout_logger.handle_sme_timeout_event().await;
242 }
243 }
244 }
245 _ = telemetry_interval.next() => {
246 connect_disconnect.handle_periodic_telemetry().await;
247 client_iface_counters_logger.handle_periodic_telemetry().await;
248 }
249 }
250 }
251 };
252 let fut = future::try_join4(
253 fut,
254 time_series_fut,
255 driver_counters_time_series_fut,
256 driver_gauges_time_series_fut,
257 )
258 .map_ok(|((), (), (), ())| ());
259 (sender, fut)
260}