1use fidl::endpoints::{ClientEnd, Proxy};
6use fidl_fuchsia_bluetooth as fidl_bt;
7use fidl_fuchsia_bluetooth_bredr as bredr;
8use fuchsia_sync::Mutex;
9use futures::sink::Sink;
10use futures::stream::{FusedStream, Stream};
11use futures::{Future, StreamExt};
12use log::warn;
13use std::fmt;
14use std::pin::Pin;
15use std::sync::Arc;
16use std::task::{Context, Poll};
17
18use crate::error::Error;
19
20pub mod socket;
21
22use socket::SocketConnection;
25
26#[derive(PartialEq, Debug, Clone)]
28pub enum ChannelMode {
29 Basic,
30 EnhancedRetransmissionMode,
31 LeCreditBasedFlowControl,
32 EnhancedCreditBasedFlowControl,
33}
34
35impl fmt::Display for ChannelMode {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 ChannelMode::Basic => write!(f, "Basic"),
39 ChannelMode::EnhancedRetransmissionMode => write!(f, "ERTM"),
40 ChannelMode::LeCreditBasedFlowControl => write!(f, "LE_Credit"),
41 ChannelMode::EnhancedCreditBasedFlowControl => write!(f, "Credit"),
42 }
43 }
44}
45
46pub enum A2dpDirection {
47 Normal,
48 Source,
49 Sink,
50}
51
52impl From<A2dpDirection> for bredr::A2dpDirectionPriority {
53 fn from(pri: A2dpDirection) -> Self {
54 match pri {
55 A2dpDirection::Normal => bredr::A2dpDirectionPriority::Normal,
56 A2dpDirection::Source => bredr::A2dpDirectionPriority::Source,
57 A2dpDirection::Sink => bredr::A2dpDirectionPriority::Sink,
58 }
59 }
60}
61
62impl TryFrom<fidl_bt::ChannelMode> for ChannelMode {
63 type Error = Error;
64 fn try_from(fidl: fidl_bt::ChannelMode) -> Result<Self, Error> {
65 match fidl {
66 fidl_bt::ChannelMode::Basic => Ok(ChannelMode::Basic),
67 fidl_bt::ChannelMode::EnhancedRetransmission => {
68 Ok(ChannelMode::EnhancedRetransmissionMode)
69 }
70 fidl_bt::ChannelMode::LeCreditBasedFlowControl => {
71 Ok(ChannelMode::LeCreditBasedFlowControl)
72 }
73 fidl_bt::ChannelMode::EnhancedCreditBasedFlowControl => {
74 Ok(ChannelMode::EnhancedCreditBasedFlowControl)
75 }
76 x => Err(Error::FailedConversion(format!("Unsupported channel mode type: {x:?}"))),
77 }
78 }
79}
80
81impl From<ChannelMode> for fidl_bt::ChannelMode {
82 fn from(x: ChannelMode) -> Self {
83 match x {
84 ChannelMode::Basic => fidl_bt::ChannelMode::Basic,
85 ChannelMode::EnhancedRetransmissionMode => fidl_bt::ChannelMode::EnhancedRetransmission,
86 ChannelMode::LeCreditBasedFlowControl => fidl_bt::ChannelMode::LeCreditBasedFlowControl,
87 ChannelMode::EnhancedCreditBasedFlowControl => {
88 fidl_bt::ChannelMode::EnhancedCreditBasedFlowControl
89 }
90 }
91 }
92}
93
94pub enum ConnectionBackendType {
95 Socket,
96 FidlClient,
97 FidlServer,
98}
99
100pub trait Connection:
104 Stream<Item = Result<Vec<u8>, zx::Status>>
105 + Sink<Vec<u8>, Error = zx::Status>
106 + Send
107 + Sync
108 + std::fmt::Debug
109 + Unpin
110{
111 fn closed<'a>(&'a self) -> Pin<Box<dyn Future<Output = Result<(), zx::Status>> + 'a>>;
113
114 fn connection_type(&self) -> ConnectionBackendType;
116
117 fn write(&self, bytes: &[u8]) -> Result<usize, zx::Status>;
120
121 fn is_closed(&self) -> bool;
123
124 fn into_fidl_channel(self: Box<Self>) -> Result<bredr::Channel, zx::Status>;
127}
128
129#[derive(Debug)]
131pub struct Channel {
132 pub(crate) connection: Box<dyn Connection>,
133 mode: ChannelMode,
134 max_tx_size: usize,
135 flush_timeout: Arc<Mutex<Option<zx::MonotonicDuration>>>,
136 audio_direction_ext: Option<bredr::AudioDirectionExtProxy>,
137 l2cap_parameters_ext: Option<bredr::L2capParametersExtProxy>,
138 audio_offload_ext: Option<bredr::AudioOffloadExtProxy>,
139 terminated: bool,
140}
141
142impl Channel {
143 pub const DEFAULT_MAX_TX: usize = 672;
144
145 pub fn from_socket(socket: zx::Socket, max_tx_size: usize) -> Result<Self, zx::Status> {
146 let connection = Box::new(SocketConnection::new(socket));
147 Ok(Channel {
148 connection,
149 mode: ChannelMode::Basic,
150 max_tx_size,
151 flush_timeout: Arc::new(Mutex::new(None)),
152 audio_direction_ext: None,
153 l2cap_parameters_ext: None,
154 audio_offload_ext: None,
155 terminated: false,
156 })
157 }
158
159 pub fn from_socket_infallible(socket: zx::Socket, max_tx_size: usize) -> Self {
160 Self::from_socket(socket, max_tx_size).unwrap()
161 }
162
163 pub fn create() -> (Self, Self) {
164 Self::create_with_max_tx(Self::DEFAULT_MAX_TX)
165 }
166
167 pub fn create_with_max_tx(max_tx_size: usize) -> (Self, Self) {
168 let (remote, local) = zx::Socket::create_datagram();
169 (
170 Channel::from_socket(remote, max_tx_size).unwrap(),
171 Channel::from_socket(local, max_tx_size).unwrap(),
172 )
173 }
174
175 pub fn max_tx_size(&self) -> usize {
176 self.max_tx_size
177 }
178
179 pub fn channel_mode(&self) -> &ChannelMode {
180 &self.mode
181 }
182
183 pub fn flush_timeout(&self) -> Option<zx::MonotonicDuration> {
184 self.flush_timeout.lock().clone()
185 }
186
187 pub fn closed<'a>(&'a self) -> impl Future<Output = Result<(), zx::Status>> + 'a {
188 self.connection.closed()
189 }
190
191 pub fn is_closed(&self) -> bool {
192 self.connection.is_closed()
193 }
194
195 pub fn write(&self, bytes: &[u8]) -> Result<usize, zx::Status> {
196 self.connection.write(bytes)
197 }
198
199 pub fn set_audio_priority(
200 &self,
201 dir: A2dpDirection,
202 ) -> impl Future<Output = Result<(), Error>> + use<> {
203 let proxy = self.audio_direction_ext.clone();
204 async move {
205 match proxy {
206 None => return Err(Error::profile("audio priority not supported")),
207 Some(proxy) => proxy
208 .set_priority(dir.into())
209 .await?
210 .map_err(|e| Error::profile(format!("setting priority failed: {e:?}"))),
211 }
212 }
213 }
214
215 pub fn set_flush_timeout(
216 &self,
217 duration: Option<zx::MonotonicDuration>,
218 ) -> impl Future<Output = Result<Option<zx::MonotonicDuration>, Error>> + use<> {
219 let flush_timeout = self.flush_timeout.clone();
220 let current = self.flush_timeout.lock().clone();
221 let proxy = self.l2cap_parameters_ext.clone();
222 async move {
223 match (current, duration) {
224 (None, None) => return Ok(None),
225 (Some(old), Some(new)) if (old - new).into_millis().abs() < 2 => {
226 return Ok(current);
227 }
228 _ => {}
229 };
230 let proxy =
231 proxy.ok_or_else(|| Error::profile("l2cap parameter changing not supported"))?;
232 let parameters = fidl_bt::ChannelParameters {
233 flush_timeout: duration.clone().map(zx::MonotonicDuration::into_nanos),
234 ..Default::default()
235 };
236 let new_params = proxy.request_parameters(¶meters).await?;
237 let new_timeout = new_params.flush_timeout.map(zx::MonotonicDuration::from_nanos);
238 *(flush_timeout.lock()) = new_timeout.clone();
239 Ok(new_timeout)
240 }
241 }
242
243 pub fn audio_offload(&self) -> Option<bredr::AudioOffloadExtProxy> {
244 self.audio_offload_ext.clone()
245 }
246}
247
248impl Stream for Channel {
249 type Item = Result<Vec<u8>, zx::Status>;
250
251 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
252 let this = self.get_mut();
253 if this.terminated {
254 warn!("Stream was polled after termination");
255 return Poll::Ready(None);
256 }
257 let res = this.connection.poll_next_unpin(cx);
258 if let Poll::Ready(None) = res {
259 this.terminated = true;
260 }
261 res
262 }
263}
264
265impl FusedStream for Channel {
266 fn is_terminated(&self) -> bool {
267 self.terminated
268 }
269}
270
271impl Sink<Vec<u8>> for Channel {
272 type Error = zx::Status;
273
274 fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
275 Pin::new(&mut *self.get_mut().connection).poll_ready(cx)
276 }
277
278 fn start_send(self: Pin<&mut Self>, item: Vec<u8>) -> Result<(), Self::Error> {
279 Pin::new(&mut *self.get_mut().connection).start_send(item)
280 }
281
282 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
283 Pin::new(&mut *self.get_mut().connection).poll_flush(cx)
284 }
285
286 fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
287 Pin::new(&mut *self.get_mut().connection).poll_close(cx)
288 }
289}
290
291impl TryFrom<Channel> for bredr::Channel {
292 type Error = Error;
293
294 fn try_from(channel: Channel) -> Result<Self, Self::Error> {
295 let mut fidl_channel = channel
296 .connection
297 .into_fidl_channel()
298 .map_err(|e| Error::profile(format!("Failed to convert to FIDL channel: {e:?}")))?;
299
300 fidl_channel.channel_mode = Some(channel.mode.into());
301 fidl_channel.max_tx_sdu_size = Some(channel.max_tx_size as u16);
302
303 let flush_timeout = channel.flush_timeout.lock().clone();
304 fidl_channel.flush_timeout = flush_timeout.map(zx::MonotonicDuration::into_nanos);
305
306 fidl_channel.ext_direction = channel
307 .audio_direction_ext
308 .map(|proxy| {
309 let chan = proxy.into_channel()?;
310 Ok(ClientEnd::new(chan.into()))
311 })
312 .transpose()
313 .map_err(|_: bredr::AudioDirectionExtProxy| {
314 Error::profile("AudioDirection proxy in use")
315 })?;
316
317 fidl_channel.ext_l2cap = channel
318 .l2cap_parameters_ext
319 .map(|proxy| {
320 let chan = proxy.into_channel()?;
321 Ok(ClientEnd::new(chan.into()))
322 })
323 .transpose()
324 .map_err(|_: bredr::L2capParametersExtProxy| {
325 Error::profile("l2cap parameters proxy in use")
326 })?;
327
328 fidl_channel.ext_audio_offload = channel
329 .audio_offload_ext
330 .map(|proxy| {
331 let chan = proxy.into_channel()?;
332 Ok(ClientEnd::new(chan.into()))
333 })
334 .transpose()
335 .map_err(|_: bredr::AudioOffloadExtProxy| {
336 Error::profile("audio offload proxy in use")
337 })?;
338
339 Ok(fidl_channel)
340 }
341}
342
343impl TryFrom<fidl_fuchsia_bluetooth_bredr::Channel> for Channel {
344 type Error = zx::Status;
345
346 fn try_from(fidl: bredr::Channel) -> Result<Self, Self::Error> {
347 let mode = match fidl.channel_mode.unwrap_or(fidl_bt::ChannelMode::Basic).try_into() {
348 Err(e) => {
349 warn!("Unsupported channel mode type: {e:?}");
350 return Err(zx::Status::INTERNAL);
351 }
352 Ok(c) => c,
353 };
354
355 let socket = fidl.socket.ok_or(zx::Status::INVALID_ARGS)?;
356 let connection = Box::new(SocketConnection::new(socket));
357
358 Ok(Self {
359 connection,
360 mode,
361 max_tx_size: fidl.max_tx_sdu_size.ok_or(zx::Status::INVALID_ARGS)? as usize,
362 flush_timeout: Arc::new(Mutex::new(
363 fidl.flush_timeout.map(zx::MonotonicDuration::from_nanos),
364 )),
365 audio_direction_ext: fidl.ext_direction.map(|e| e.into_proxy()),
366 l2cap_parameters_ext: fidl.ext_l2cap.map(|e| e.into_proxy()),
367 audio_offload_ext: fidl.ext_audio_offload.map(|c| c.into_proxy()),
368 terminated: false,
369 })
370 }
371}