1use fuchsia_bluetooth::profile::ValidScoConnectionParameters;
6use fuchsia_inspect as inspect;
7use fuchsia_inspect_derive::{IValue, Unit};
8use futures::future::{BoxFuture, Fuse, FusedFuture, Future, Shared};
9use futures::FutureExt;
10use std::fmt;
11use vigil::Vigil;
12
13use crate::a2dp;
14use crate::sco::{ConnectError, Connection};
15
16#[derive(Debug)]
17pub struct Active {
18 pub params: ValidScoConnectionParameters,
19 on_closed: Shared<BoxFuture<'static, ()>>,
20 _pause_token: a2dp::PauseToken,
22}
23
24impl Active {
25 pub fn new(connection: &Connection, pause_token: a2dp::PauseToken) -> Self {
26 let on_closed = connection.on_closed().boxed().shared();
27 Self { params: connection.params.clone(), on_closed, _pause_token: pause_token }
28 }
29
30 pub fn on_closed(&self) -> impl Future<Output = ()> + 'static {
31 self.on_closed.clone()
32 }
33}
34
35impl Unit for Active {
36 type Data = <Connection as Unit>::Data;
37 fn inspect_create(&self, parent: &inspect::Node, name: impl AsRef<str>) -> Self::Data {
38 self.params.inspect_create(parent, name)
39 }
40
41 fn inspect_update(&self, data: &mut Self::Data) {
42 self.params.inspect_update(data)
43 }
44}
45
46#[derive(Default)]
47pub enum State {
48 #[default]
50 Inactive,
51 SettingUp,
56 TearingDown,
61 AwaitingRemote(BoxFuture<'static, Result<Connection, ConnectError>>),
63 Active(Vigil<Active>),
65}
66
67impl State {
68 pub fn is_active(&self) -> bool {
69 match self {
70 Self::Active(_) => true,
71 _ => false,
72 }
73 }
74
75 pub fn on_connected<'a>(
76 &'a mut self,
77 ) -> impl Future<Output = Result<Connection, ConnectError>> + FusedFuture + 'a {
78 match self {
79 Self::AwaitingRemote(ref mut fut) => fut.fuse(),
80 _ => Fuse::terminated(),
81 }
82 }
83}
84
85impl fmt::Debug for State {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 State::Inactive => write!(f, "Inactive"),
89 State::SettingUp => write!(f, "SettingUp"),
90 State::TearingDown => write!(f, "TearingDown"),
91 State::AwaitingRemote(_) => write!(f, "AwaitingRemote"),
92 State::Active(active) => write!(f, "Active({:?})", active),
93 }
94 }
95}
96
97impl std::fmt::Display for State {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 let state = match &self {
100 State::Inactive => "Inactive",
101 State::SettingUp => "SettingUp",
102 State::TearingDown => "TearingDown",
103 State::AwaitingRemote(_) => "AwaitingRemote",
104 State::Active(_) => "Active",
105 };
106 write!(f, "{}", state)
107 }
108}
109
110impl Unit for State {
111 type Data = inspect::Node;
112 fn inspect_create(&self, parent: &inspect::Node, name: impl AsRef<str>) -> Self::Data {
113 let mut node = parent.create_child(String::from(name.as_ref()));
114 self.inspect_update(&mut node);
115 node
116 }
117
118 fn inspect_update(&self, data: &mut Self::Data) {
119 data.clear_recorded();
120 data.record_string("status", &format!("{}", self));
121 if let State::Active(active) = &self {
122 let node = active.inspect_create(data, "parameters");
123 data.record(node);
124 }
125 }
126}
127
128pub type InspectableState = IValue<State>;
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 use diagnostics_assertions::assert_data_tree;
135 use fuchsia_bluetooth::types::PeerId;
136 use fuchsia_inspect_derive::WithInspect;
137 use {fidl_fuchsia_bluetooth as fidl_bt, fidl_fuchsia_bluetooth_bredr as bredr};
138
139 #[fuchsia::test]
140 async fn sco_state_inspect_tree() {
141 let inspect = inspect::Inspector::default();
142
143 let mut state =
144 InspectableState::default().with_inspect(inspect.root(), "sco_connection").unwrap();
145 assert_data_tree!(inspect, root: {
147 sco_connection: {
148 status: "Inactive",
149 }
150 });
151
152 state.iset(State::SettingUp);
153 assert_data_tree!(inspect, root: {
154 sco_connection: {
155 status: "SettingUp",
156 }
157 });
158
159 state.iset(State::AwaitingRemote(Box::pin(async { Err(ConnectError::Failed) })));
160 assert_data_tree!(inspect, root: {
161 sco_connection: {
162 status: "AwaitingRemote",
163 }
164 });
165
166 let params = bredr::ScoConnectionParameters {
167 parameter_set: Some(bredr::HfpParameterSet::D1),
168 air_coding_format: Some(fidl_bt::AssignedCodingFormat::Cvsd),
169 air_frame_size: Some(60),
170 io_bandwidth: Some(16000),
171 io_coding_format: Some(fidl_bt::AssignedCodingFormat::LinearPcm),
172 io_frame_size: Some(16),
173 io_pcm_data_format: Some(fidl_fuchsia_hardware_audio::SampleFormat::PcmSigned),
174 io_pcm_sample_payload_msb_position: Some(1),
175 path: Some(bredr::DataPath::Offload),
176 ..Default::default()
177 };
178 let (sco_proxy, _sco_stream) =
179 fidl::endpoints::create_proxy_and_stream::<bredr::ScoConnectionMarker>();
180 let connection = Connection::build(PeerId(1), params, sco_proxy);
181 let vigil = Vigil::new(Active::new(&connection, None));
182 state.iset(State::Active(vigil));
183 assert_data_tree!(inspect, root: {
184 sco_connection: {
185 status: "Active",
186 parameters: {
187 parameter_set: "D1",
188 air_coding_format: "Cvsd",
189 air_frame_size: 60u64,
190 io_bandwidth: 16000u64,
191 io_coding_format: "LinearPcm",
192 io_frame_size: 16u64,
193 io_pcm_data_format: "PcmSigned",
194 io_pcm_sample_payload_msb_position: 1u64,
195 path: "Offload",
196 },
197 }
198 });
199
200 state.iset(State::TearingDown);
201 assert_data_tree!(inspect, root: {
202 sco_connection: {
203 status: "TearingDown",
204 }
205 });
206 }
207}