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