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 fidl_client;
21pub mod fidl_server;
22pub mod socket;
23
24use fidl_client::FidlClientConnection;
25use fidl_server::FidlServerConnection;
26use socket::SocketConnection;
27
28pub(crate) const MAX_BATCH_SIZE_BYTES: usize = 60 * 1024;
31
32pub(crate) const PACKET_OVERHEAD: usize = 24;
35
36#[derive(PartialEq, Debug, Clone)]
38pub enum ChannelMode {
39 Basic,
40 EnhancedRetransmissionMode,
41 LeCreditBasedFlowControl,
42 EnhancedCreditBasedFlowControl,
43}
44
45impl fmt::Display for ChannelMode {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 ChannelMode::Basic => write!(f, "Basic"),
49 ChannelMode::EnhancedRetransmissionMode => write!(f, "ERTM"),
50 ChannelMode::LeCreditBasedFlowControl => write!(f, "LE_Credit"),
51 ChannelMode::EnhancedCreditBasedFlowControl => write!(f, "Credit"),
52 }
53 }
54}
55
56pub enum A2dpDirection {
57 Normal,
58 Source,
59 Sink,
60}
61
62impl From<A2dpDirection> for bredr::A2dpDirectionPriority {
63 fn from(pri: A2dpDirection) -> Self {
64 match pri {
65 A2dpDirection::Normal => bredr::A2dpDirectionPriority::Normal,
66 A2dpDirection::Source => bredr::A2dpDirectionPriority::Source,
67 A2dpDirection::Sink => bredr::A2dpDirectionPriority::Sink,
68 }
69 }
70}
71
72impl TryFrom<fidl_bt::ChannelMode> for ChannelMode {
73 type Error = Error;
74 fn try_from(fidl: fidl_bt::ChannelMode) -> Result<Self, Error> {
75 match fidl {
76 fidl_bt::ChannelMode::Basic => Ok(ChannelMode::Basic),
77 fidl_bt::ChannelMode::EnhancedRetransmission => {
78 Ok(ChannelMode::EnhancedRetransmissionMode)
79 }
80 fidl_bt::ChannelMode::LeCreditBasedFlowControl => {
81 Ok(ChannelMode::LeCreditBasedFlowControl)
82 }
83 fidl_bt::ChannelMode::EnhancedCreditBasedFlowControl => {
84 Ok(ChannelMode::EnhancedCreditBasedFlowControl)
85 }
86 x => Err(Error::FailedConversion(format!("Unsupported channel mode type: {x:?}"))),
87 }
88 }
89}
90
91impl From<ChannelMode> for fidl_bt::ChannelMode {
92 fn from(x: ChannelMode) -> Self {
93 match x {
94 ChannelMode::Basic => fidl_bt::ChannelMode::Basic,
95 ChannelMode::EnhancedRetransmissionMode => fidl_bt::ChannelMode::EnhancedRetransmission,
96 ChannelMode::LeCreditBasedFlowControl => fidl_bt::ChannelMode::LeCreditBasedFlowControl,
97 ChannelMode::EnhancedCreditBasedFlowControl => {
98 fidl_bt::ChannelMode::EnhancedCreditBasedFlowControl
99 }
100 }
101 }
102}
103
104#[derive(PartialEq, Clone, Copy, Debug)]
105pub enum ConnectionBackendType {
106 Socket,
107 FidlClient,
108 FidlServer,
109}
110
111pub trait Connection:
115 Stream<Item = Result<Vec<u8>, zx::Status>>
116 + Sink<Vec<u8>, Error = zx::Status>
117 + Send
118 + Sync
119 + std::fmt::Debug
120 + Unpin
121{
122 fn closed<'a>(&'a self) -> Pin<Box<dyn Future<Output = Result<(), zx::Status>> + 'a>>;
124
125 fn connection_type(&self) -> ConnectionBackendType;
127
128 fn write(&self, bytes: &[u8]) -> Result<usize, zx::Status>;
131
132 fn is_closed(&self) -> bool;
134
135 fn into_fidl_channel(self: Box<Self>) -> Result<bredr::Channel, zx::Status>;
138}
139
140#[derive(Debug)]
142pub struct Channel {
143 pub(crate) connection: Box<dyn Connection>,
144 mode: ChannelMode,
145 max_tx_size: usize,
146 flush_timeout: Arc<Mutex<Option<zx::MonotonicDuration>>>,
147 audio_direction_ext: Option<bredr::AudioDirectionExtProxy>,
148 l2cap_parameters_ext: Option<bredr::L2capParametersExtProxy>,
149 audio_offload_ext: Option<bredr::AudioOffloadExtProxy>,
150 terminated: bool,
151}
152
153impl Channel {
154 pub const DEFAULT_MAX_TX: usize = 672;
155
156 pub fn from_socket(socket: zx::Socket, max_tx_size: usize) -> Result<Self, zx::Status> {
157 let connection = Box::new(SocketConnection::new(socket));
158 Ok(Channel {
159 connection,
160 mode: ChannelMode::Basic,
161 max_tx_size,
162 flush_timeout: Arc::new(Mutex::new(None)),
163 audio_direction_ext: None,
164 l2cap_parameters_ext: None,
165 audio_offload_ext: None,
166 terminated: false,
167 })
168 }
169
170 pub fn from_fidl_client(proxy: fidl_bt::ChannelProxy, max_tx_size: usize) -> Self {
171 let connection = Box::new(FidlClientConnection::new(proxy, max_tx_size));
172 Channel {
173 connection,
174 mode: ChannelMode::Basic,
175 max_tx_size,
176 flush_timeout: Arc::new(Mutex::new(None)),
177 audio_direction_ext: None,
178 l2cap_parameters_ext: None,
179 audio_offload_ext: None,
180 terminated: false,
181 }
182 }
183
184 pub fn from_fidl_server(
185 request_stream: fidl_bt::ChannelRequestStream,
186 max_tx_size: usize,
187 ) -> Self {
188 let connection = Box::new(FidlServerConnection::new(request_stream, max_tx_size));
189 Channel {
190 connection,
191 mode: ChannelMode::Basic,
192 max_tx_size,
193 flush_timeout: Arc::new(Mutex::new(None)),
194 audio_direction_ext: None,
195 l2cap_parameters_ext: None,
196 audio_offload_ext: None,
197 terminated: false,
198 }
199 }
200
201 pub fn from_socket_infallible(socket: zx::Socket, max_tx_size: usize) -> Self {
202 Self::from_socket(socket, max_tx_size).unwrap()
203 }
204
205 pub fn create_socket_pair() -> (Self, Self) {
206 Self::create_socket_pair_with_max_tx(Self::DEFAULT_MAX_TX)
207 }
208
209 pub fn create_socket_pair_with_max_tx(max_tx_size: usize) -> (Self, Self) {
210 let (remote, local) = zx::Socket::create_datagram();
211 (
212 Channel::from_socket(remote, max_tx_size).unwrap(),
213 Channel::from_socket(local, max_tx_size).unwrap(),
214 )
215 }
216
217 pub fn max_tx_size(&self) -> usize {
218 self.max_tx_size
219 }
220
221 pub fn connection_type(&self) -> ConnectionBackendType {
222 self.connection.connection_type()
223 }
224
225 pub fn channel_mode(&self) -> &ChannelMode {
226 &self.mode
227 }
228
229 pub fn flush_timeout(&self) -> Option<zx::MonotonicDuration> {
230 self.flush_timeout.lock().clone()
231 }
232
233 pub fn closed<'a>(&'a self) -> impl Future<Output = Result<(), zx::Status>> + 'a {
234 self.connection.closed()
235 }
236
237 pub fn is_closed(&self) -> bool {
238 self.connection.is_closed()
239 }
240
241 pub fn write(&self, bytes: &[u8]) -> Result<usize, zx::Status> {
242 self.connection.write(bytes)
243 }
244
245 pub fn set_audio_priority(
246 &self,
247 dir: A2dpDirection,
248 ) -> impl Future<Output = Result<(), Error>> + use<> {
249 let proxy = self.audio_direction_ext.clone();
250 async move {
251 match proxy {
252 None => return Err(Error::profile("audio priority not supported")),
253 Some(proxy) => proxy
254 .set_priority(dir.into())
255 .await?
256 .map_err(|e| Error::profile(format!("setting priority failed: {e:?}"))),
257 }
258 }
259 }
260
261 pub fn set_flush_timeout(
262 &self,
263 duration: Option<zx::MonotonicDuration>,
264 ) -> impl Future<Output = Result<Option<zx::MonotonicDuration>, Error>> + use<> {
265 let flush_timeout = self.flush_timeout.clone();
266 let current = self.flush_timeout.lock().clone();
267 let proxy = self.l2cap_parameters_ext.clone();
268 async move {
269 match (current, duration) {
270 (None, None) => return Ok(None),
271 (Some(old), Some(new)) if (old - new).into_millis().abs() < 2 => {
272 return Ok(current);
273 }
274 _ => {}
275 };
276 let proxy =
277 proxy.ok_or_else(|| Error::profile("l2cap parameter changing not supported"))?;
278 let parameters = fidl_bt::ChannelParameters {
279 flush_timeout: duration.clone().map(zx::MonotonicDuration::into_nanos),
280 ..Default::default()
281 };
282 let new_params = proxy.request_parameters(¶meters).await?;
283 let new_timeout = new_params.flush_timeout.map(zx::MonotonicDuration::from_nanos);
284 *(flush_timeout.lock()) = new_timeout.clone();
285 Ok(new_timeout)
286 }
287 }
288
289 pub fn audio_offload(&self) -> Option<bredr::AudioOffloadExtProxy> {
290 self.audio_offload_ext.clone()
291 }
292}
293
294impl Stream for Channel {
295 type Item = Result<Vec<u8>, zx::Status>;
296
297 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
298 let this = self.get_mut();
299 if this.terminated {
300 warn!("Stream was polled after termination");
301 return Poll::Ready(None);
302 }
303 let res = this.connection.poll_next_unpin(cx);
304 if let Poll::Ready(None) = res {
305 this.terminated = true;
306 }
307 res
308 }
309}
310
311impl FusedStream for Channel {
312 fn is_terminated(&self) -> bool {
313 self.terminated
314 }
315}
316
317impl Sink<Vec<u8>> for Channel {
318 type Error = zx::Status;
319
320 fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
321 Pin::new(&mut *self.get_mut().connection).poll_ready(cx)
322 }
323
324 fn start_send(self: Pin<&mut Self>, item: Vec<u8>) -> Result<(), Self::Error> {
325 Pin::new(&mut *self.get_mut().connection).start_send(item)
326 }
327
328 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
329 Pin::new(&mut *self.get_mut().connection).poll_flush(cx)
330 }
331
332 fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
333 Pin::new(&mut *self.get_mut().connection).poll_close(cx)
334 }
335}
336
337impl TryFrom<Channel> for bredr::Channel {
338 type Error = Error;
339
340 fn try_from(channel: Channel) -> Result<Self, Self::Error> {
341 let mut fidl_channel = channel
342 .connection
343 .into_fidl_channel()
344 .map_err(|e| Error::profile(format!("Failed to convert to FIDL channel: {e:?}")))?;
345
346 fidl_channel.channel_mode = Some(channel.mode.into());
347 fidl_channel.max_tx_sdu_size = Some(channel.max_tx_size as u16);
348
349 let flush_timeout = channel.flush_timeout.lock().clone();
350 fidl_channel.flush_timeout = flush_timeout.map(zx::MonotonicDuration::into_nanos);
351
352 fidl_channel.ext_direction = channel
353 .audio_direction_ext
354 .map(|proxy| {
355 let chan = proxy.into_channel()?;
356 Ok(ClientEnd::new(chan.into()))
357 })
358 .transpose()
359 .map_err(|_: bredr::AudioDirectionExtProxy| {
360 Error::profile("AudioDirection proxy in use")
361 })?;
362
363 fidl_channel.ext_l2cap = channel
364 .l2cap_parameters_ext
365 .map(|proxy| {
366 let chan = proxy.into_channel()?;
367 Ok(ClientEnd::new(chan.into()))
368 })
369 .transpose()
370 .map_err(|_: bredr::L2capParametersExtProxy| {
371 Error::profile("l2cap parameters proxy in use")
372 })?;
373
374 fidl_channel.ext_audio_offload = channel
375 .audio_offload_ext
376 .map(|proxy| {
377 let chan = proxy.into_channel()?;
378 Ok(ClientEnd::new(chan.into()))
379 })
380 .transpose()
381 .map_err(|_: bredr::AudioOffloadExtProxy| {
382 Error::profile("audio offload proxy in use")
383 })?;
384
385 Ok(fidl_channel)
386 }
387}
388
389impl TryFrom<fidl_fuchsia_bluetooth_bredr::Channel> for Channel {
390 type Error = zx::Status;
391
392 fn try_from(fidl: bredr::Channel) -> Result<Self, Self::Error> {
393 let mode = match fidl.channel_mode.unwrap_or(fidl_bt::ChannelMode::Basic).try_into() {
394 Err(e) => {
395 warn!("Unsupported channel mode type: {e:?}");
396 return Err(zx::Status::INTERNAL);
397 }
398 Ok(c) => c,
399 };
400
401 let max_tx_size = fidl.max_tx_sdu_size.ok_or(zx::Status::INVALID_ARGS)? as usize;
402
403 let connection: Box<dyn Connection> = if let Some(conn) = fidl.connection {
404 let proxy = conn.into_proxy();
405 Box::new(FidlClientConnection::new(proxy, max_tx_size)) as Box<dyn Connection>
406 } else if let Some(socket) = fidl.socket {
407 Box::new(SocketConnection::new(socket)) as Box<dyn Connection>
408 } else {
409 return Err(zx::Status::INVALID_ARGS);
410 };
411
412 Ok(Self {
413 connection,
414 mode,
415 max_tx_size,
416 flush_timeout: Arc::new(Mutex::new(
417 fidl.flush_timeout.map(zx::MonotonicDuration::from_nanos),
418 )),
419 audio_direction_ext: fidl.ext_direction.map(|e| e.into_proxy()),
420 l2cap_parameters_ext: fidl.ext_l2cap.map(|e| e.into_proxy()),
421 audio_offload_ext: fidl.ext_audio_offload.map(|c| c.into_proxy()),
422 terminated: false,
423 })
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use fidl::endpoints::create_request_stream;
431 use fidl_fuchsia_bluetooth as fidl_bt;
432 use fidl_fuchsia_bluetooth_bredr as bredr;
433 use fuchsia_async as fasync;
434 use futures::StreamExt;
435 use std::pin::pin;
436
437 fn build_socket_bredr_channel() -> (bredr::Channel, zx::Socket) {
438 let (remote, local) = zx::Socket::create_datagram();
439 (
440 bredr::Channel {
441 socket: Some(remote),
442 channel_mode: Some(fidl_bt::ChannelMode::Basic),
443 max_tx_sdu_size: Some(1004),
444 ..Default::default()
445 },
446 local,
447 )
448 }
449
450 #[test]
451 fn direction_ext() {
452 let mut exec = fasync::TestExecutor::new();
453
454 let (no_ext, _local) = build_socket_bredr_channel();
455 let channel = Channel::try_from(no_ext).unwrap();
456
457 assert!(
458 exec.run_singlethreaded(channel.set_audio_priority(A2dpDirection::Normal)).is_err()
459 );
460 assert!(exec.run_singlethreaded(channel.set_audio_priority(A2dpDirection::Sink)).is_err());
461
462 let (mut ext, _local) = build_socket_bredr_channel();
463 let (client_end, mut direction_request_stream) =
464 create_request_stream::<bredr::AudioDirectionExtMarker>();
465 ext.ext_direction = Some(client_end);
466
467 let channel = Channel::try_from(ext).unwrap();
468
469 let audio_direction_fut = channel.set_audio_priority(A2dpDirection::Normal);
470 let mut audio_direction_fut = pin!(audio_direction_fut);
471
472 assert!(exec.run_until_stalled(&mut audio_direction_fut).is_pending());
473
474 match exec.run_until_stalled(&mut direction_request_stream.next()) {
475 Poll::Ready(Some(Ok(bredr::AudioDirectionExtRequest::SetPriority {
476 priority,
477 responder,
478 }))) => {
479 assert_eq!(bredr::A2dpDirectionPriority::Normal, priority);
480 responder.send(Ok(())).expect("response to send cleanly");
481 }
482 x => panic!("Expected a item to be ready on the request stream, got {:?}", x),
483 };
484
485 match exec.run_until_stalled(&mut audio_direction_fut) {
486 Poll::Ready(Ok(())) => {}
487 _x => panic!("Expected ok result from audio direction response"),
488 };
489
490 let audio_direction_fut = channel.set_audio_priority(A2dpDirection::Sink);
491 let mut audio_direction_fut = pin!(audio_direction_fut);
492
493 assert!(exec.run_until_stalled(&mut audio_direction_fut).is_pending());
494
495 match exec.run_until_stalled(&mut direction_request_stream.next()) {
496 Poll::Ready(Some(Ok(bredr::AudioDirectionExtRequest::SetPriority {
497 priority,
498 responder,
499 }))) => {
500 assert_eq!(bredr::A2dpDirectionPriority::Sink, priority);
501 responder
502 .send(Err(fidl_fuchsia_bluetooth::ErrorCode::Failed))
503 .expect("response to send cleanly");
504 }
505 x => panic!("Expected a item to be ready on the request stream, got {:?}", x),
506 };
507
508 match exec.run_until_stalled(&mut audio_direction_fut) {
509 Poll::Ready(Err(_)) => {}
510 _x => panic!("Expected error result from audio direction response"),
511 };
512 }
513
514 #[test]
515 fn flush_timeout() {
516 let mut exec = fasync::TestExecutor::new();
517
518 let (mut no_ext, _local) = build_socket_bredr_channel();
519 no_ext.flush_timeout = Some(50_000_000); let channel = Channel::try_from(no_ext).unwrap();
521
522 assert_eq!(Some(zx::MonotonicDuration::from_millis(50)), channel.flush_timeout());
523
524 let res = exec.run_singlethreaded(
526 channel.set_flush_timeout(Some(zx::MonotonicDuration::from_millis(49))),
527 );
528 assert_eq!(Some(zx::MonotonicDuration::from_millis(50)), res.expect("shouldn't error"));
529 let res = exec.run_singlethreaded(
530 channel.set_flush_timeout(Some(zx::MonotonicDuration::from_millis(51))),
531 );
532 assert_eq!(Some(zx::MonotonicDuration::from_millis(50)), res.expect("shouldn't error"));
533
534 assert!(
535 exec.run_singlethreaded(
536 channel.set_flush_timeout(Some(zx::MonotonicDuration::from_millis(200)))
537 )
538 .is_err()
539 );
540 assert!(exec.run_singlethreaded(channel.set_flush_timeout(None)).is_err());
541
542 let (mut ext, _local) = build_socket_bredr_channel();
543 let (client_end, mut l2cap_request_stream) =
544 create_request_stream::<bredr::L2capParametersExtMarker>();
545 ext.ext_l2cap = Some(client_end);
546
547 let channel = Channel::try_from(ext).unwrap();
548
549 {
550 let flush_timeout_fut = channel.set_flush_timeout(None);
551 let mut flush_timeout_fut = pin!(flush_timeout_fut);
552
553 match exec.run_until_stalled(&mut flush_timeout_fut) {
555 Poll::Ready(Ok(None)) => {}
556 x => panic!("Expected no flush timeout to not stall, got {:?}", x),
557 }
558 }
559
560 let req_duration = zx::MonotonicDuration::from_millis(42);
561
562 {
563 let flush_timeout_fut = channel.set_flush_timeout(Some(req_duration));
564 let mut flush_timeout_fut = pin!(flush_timeout_fut);
565
566 assert!(exec.run_until_stalled(&mut flush_timeout_fut).is_pending());
567
568 match exec.run_until_stalled(&mut l2cap_request_stream.next()) {
569 Poll::Ready(Some(Ok(bredr::L2capParametersExtRequest::RequestParameters {
570 request,
571 responder,
572 }))) => {
573 assert_eq!(Some(req_duration.into_nanos()), request.flush_timeout);
574 let params = fidl_bt::ChannelParameters {
576 flush_timeout: Some(50_000_000), ..Default::default()
578 };
579 responder.send(¶ms).expect("response to send cleanly");
580 }
581 x => panic!("Expected a item to be ready on the request stream, got {:?}", x),
582 };
583
584 match exec.run_until_stalled(&mut flush_timeout_fut) {
585 Poll::Ready(Ok(Some(duration))) => {
586 assert_eq!(zx::MonotonicDuration::from_millis(50), duration)
587 }
588 x => panic!("Expected ready result from params response, got {:?}", x),
589 };
590 }
591
592 assert_eq!(Some(zx::MonotonicDuration::from_millis(50)), channel.flush_timeout());
594 }
595
596 #[test]
597 fn audio_offload() {
598 let _exec = fasync::TestExecutor::new();
599
600 let (no_ext, _local) = build_socket_bredr_channel();
601 let channel = Channel::try_from(no_ext).unwrap();
602
603 assert!(channel.audio_offload().is_none());
604
605 let (mut ext, _local) = build_socket_bredr_channel();
606 let (client_end, mut _audio_offload_ext_req_stream) =
607 create_request_stream::<bredr::AudioOffloadExtMarker>();
608 ext.ext_audio_offload = Some(client_end);
609
610 let channel = Channel::try_from(ext).unwrap();
611
612 let offload_ext = channel.audio_offload();
613 assert!(offload_ext.is_some());
614 assert!(channel.audio_offload().is_some());
616 drop(offload_ext);
618 assert!(channel.audio_offload().is_some());
619 }
620
621 #[test]
622 fn channel_from_fidl_priority() {
623 let _exec = fasync::TestExecutor::new();
624
625 let (client_end, _server_end) =
628 fidl::endpoints::create_endpoints::<fidl_bt::ChannelMarker>();
629 let (mut fidl_both, _socket_local) = build_socket_bredr_channel();
630 fidl_both.connection = Some(client_end);
631
632 let chan = Channel::try_from(fidl_both).expect("to convert successfully");
633 assert_eq!(chan.connection.connection_type(), ConnectionBackendType::FidlClient);
634
635 let (socket_only, _socket_local) = build_socket_bredr_channel();
638
639 let chan = Channel::try_from(socket_only).expect("to convert successfully");
640 assert_eq!(chan.connection.connection_type(), ConnectionBackendType::Socket);
641
642 let fidl_empty = bredr::Channel {
645 channel_mode: Some(fidl_bt::ChannelMode::Basic),
646 max_tx_sdu_size: Some(1004),
647 ..Default::default()
648 };
649 assert!(Channel::try_from(fidl_empty).is_err());
650 }
651}