| // Copyright 2024 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use bt_common::{PeerId, Uuid}; |
| use bt_gatt::test_utils::{FakeServer, FakeServerEvent, FakeTypes}; |
| use bt_gatt::types::Handle; |
| |
| use futures::channel::mpsc::UnboundedReceiver; |
| use futures::{Stream, StreamExt}; |
| use std::pin::Pin; |
| use std::task::{Context, Poll}; |
| |
| use crate::server::AudioStreamControlServiceServer; |
| use crate::server::ServiceEvent; |
| use crate::server::{ASCS_SERVICE_ID, ASCS_UUID}; |
| use crate::types::CodecConfiguration; |
| use crate::*; |
| |
| #[track_caller] |
| fn expect_service_event(events: &mut UnboundedReceiver<FakeServerEvent>) -> FakeServerEvent { |
| match events.poll_next_unpin(&mut futures_test::task::noop_context()) { |
| Poll::Ready(Some(event)) => event, |
| x => panic!("Expected fake server event, got {x:?}"), |
| } |
| } |
| |
| #[test] |
| fn publishes() { |
| let mut ascs_server = std::pin::pin!(AudioStreamControlServiceServer::<FakeTypes>::new(1, 1)); |
| |
| let (count_waker, woken_count) = futures_test::task::new_count_waker(); |
| |
| // Polling the server before it's published should result in Poll::Pending |
| let poll_result = ascs_server.as_mut().poll_next(&mut Context::from_waker(&count_waker)); |
| |
| assert!(poll_result.is_pending()); |
| assert_eq!(woken_count.get(), 0); |
| |
| let (fake_server, mut events) = FakeServer::new(); |
| |
| let result = ascs_server.publish(&fake_server); |
| assert!(result.is_ok()); |
| |
| let already_published = ascs_server.publish(&fake_server); |
| |
| assert!(already_published.is_err()); |
| |
| assert_eq!(woken_count.get(), 1); |
| |
| let poll_result = ascs_server.poll_next(&mut Context::from_waker(&count_waker)); |
| |
| match expect_service_event(&mut events) { |
| FakeServerEvent::Published { id: _, definition } => { |
| assert_eq!(definition.uuid(), ASCS_UUID); |
| assert_eq!( |
| definition.characteristics().filter(|c| c.uuid == Uuid::from_u16(0x2BC4)).count(), |
| 1 |
| ); |
| assert_eq!( |
| definition.characteristics().filter(|c| c.uuid == Uuid::from_u16(0x2BC5)).count(), |
| 1 |
| ); |
| } |
| x => panic!("Expected published event, got {x:?}"), |
| }; |
| |
| // Should still be pending, even though we had some work to do. |
| assert!(poll_result.is_pending()); |
| } |
| |
| fn register_for_notification(fake_server: &mut FakeServer, peer_id: PeerId, handle: Handle) { |
| fake_server.incoming_client_configuration( |
| peer_id, |
| ASCS_SERVICE_ID, |
| handle, |
| bt_gatt::server::NotificationType::Notify, |
| ); |
| } |
| |
| // A published server with one sink and one source. |
| fn published_server() -> ( |
| Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, |
| FakeServer, |
| UnboundedReceiver<FakeServerEvent>, |
| ) { |
| let mut ascs_server = Box::pin(AudioStreamControlServiceServer::<FakeTypes>::new(1, 1)); |
| let (mut fake_server, mut events) = FakeServer::new(); |
| |
| let result = ascs_server.publish(&fake_server); |
| assert!(result.is_ok()); |
| |
| assert!(ascs_server.poll_next_unpin(&mut futures_test::task::noop_context()).is_pending()); |
| |
| assert!(matches!(expect_service_event(&mut events), FakeServerEvent::Published { .. })); |
| |
| for peer_id in [PeerId(1), PeerId(2)] { |
| for handle in [Handle(1), Handle(2), Handle(3)] { |
| register_for_notification(&mut fake_server, peer_id, handle); |
| } |
| } |
| |
| (ascs_server, fake_server, events) |
| } |
| |
| fn poll_server( |
| server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, |
| ) -> Poll<Option<core::result::Result<ServiceEvent, Error>>> { |
| server.poll_next_unpin(&mut futures_test::task::noop_context()) |
| } |
| |
| #[test] |
| fn peers_are_separated() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| // Read the sink uuid |
| fake_server.incoming_read(PeerId(1), ASCS_SERVICE_ID, Handle(3), 0); |
| // Poll the ascs server, should not result in an ASCS event |
| assert!(poll_server(&mut ascs_server).is_pending()); |
| |
| // Should have the response |
| let mut peer_one_value; |
| match server_events.poll_next_unpin(&mut futures_test::task::noop_context()) { |
| Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => { |
| assert_eq!(service_id, ASCS_SERVICE_ID); |
| peer_one_value = value.unwrap(); |
| } |
| x => panic!("Expected the read to be responded to got {x:?}"), |
| }; |
| |
| let ase_id = peer_one_value[0]; |
| |
| // Codec Configure the first peer ase_id |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| vec![0x01, 0x01, ase_id, 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00], |
| ); |
| |
| use crate::server::ServiceEvent; |
| use crate::types::*; |
| // Should have a codec configure event to respond to |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::CodecConfigure { responder, .. }))) => { |
| responder.accept( |
| Framing::Unframed, |
| vec![Phy::Le1MPhy], |
| 5, |
| std::time::Duration::from_millis(20).try_into().unwrap(), |
| PresentationDelayRange::build(0, 500).unwrap(), |
| ); |
| } |
| x => panic!("Expected a CodecConfigure, got {x:?}"), |
| }; |
| |
| // Expect the write to be responded to / acknowledged and a notification from |
| // the CP handle and the Source ASE |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::WriteResponded { value, .. } => assert!(value.is_ok()), |
| x => panic!("Expected acknowledge of write, got {x:?}"), |
| }; |
| assert!(poll_server(&mut ascs_server).is_pending()); |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, .. } => assert_eq!(Handle(1), handle), |
| x => panic!("Expected acknowledge of write, got {x:?}"), |
| }; |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { peers, handle, value, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| // Should be the same ASE_ID |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x01); // State is CodecConfigured |
| peer_one_value = value; |
| } |
| x => panic!("Expected acknowledge of write, got {x:?}"), |
| }; |
| |
| // Read the sink id from another peer |
| fake_server.incoming_read(PeerId(2), ASCS_SERVICE_ID, Handle(3), 0); |
| |
| assert!(poll_server(&mut ascs_server).is_pending()); |
| assert!(poll_server(&mut ascs_server).is_pending()); |
| |
| let peer_two_value; |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::ReadResponded { service_id, handle: _, value } => { |
| assert_eq!(service_id, ASCS_SERVICE_ID); |
| peer_two_value = value.unwrap(); |
| } |
| x => panic!("Expected the read to be responded to got {x:?}"), |
| }; |
| |
| assert!(peer_one_value[1] != peer_two_value[1]); |
| } |
| |
| #[track_caller] |
| fn expect_control_point_notified( |
| ascs_server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, |
| server_events: &mut UnboundedReceiver<FakeServerEvent>, |
| ) -> Vec<u8> { |
| // Expect the write to be responded to / acknowledged and a notification from |
| // the CP handle and the Source ASE |
| match expect_service_event(server_events) { |
| FakeServerEvent::WriteResponded { value, .. } => assert!(value.is_ok()), |
| x => panic!("Expected acknowledge of write, got {x:?}"), |
| }; |
| assert!(poll_server(ascs_server).is_pending()); |
| match expect_service_event(server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(1), handle); |
| value |
| } |
| x => panic!("Expected acknowledge of write, got {x:?}"), |
| } |
| } |
| |
| #[track_caller] |
| fn find_ase_id( |
| ascs_server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, |
| fake_server: &FakeServer, |
| server_events: &mut UnboundedReceiver<FakeServerEvent>, |
| handle: Handle, |
| ) -> u8 { |
| // Read the sink uuid |
| fake_server.incoming_read(PeerId(1), ASCS_SERVICE_ID, handle, 0); |
| // Poll the ascs server, should not result in an ASCS event |
| assert!(poll_server(ascs_server).is_pending()); |
| |
| // Find the AseId |
| let sink_value; |
| match server_events.poll_next_unpin(&mut futures_test::task::noop_context()) { |
| Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => { |
| assert_eq!(service_id, ASCS_SERVICE_ID); |
| sink_value = value.unwrap(); |
| } |
| x => panic!("Expected the read to be responded to got {x:?}"), |
| }; |
| |
| let ase_id = sink_value[0]; |
| // Should start in idle. |
| assert_eq!(sink_value[1], 0x00); |
| assert_eq!(sink_value.len(), 2); |
| |
| ase_id |
| } |
| |
| #[track_caller] |
| fn find_first_ase_id( |
| ascs_server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, |
| fake_server: &FakeServer, |
| server_events: &mut UnboundedReceiver<FakeServerEvent>, |
| ) -> u8 { |
| find_ase_id(ascs_server, fake_server, server_events, Handle(3)) |
| } |
| |
| #[test] |
| fn invalid_operation() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| let ase_id = find_first_ase_id(&mut ascs_server, &fake_server, &mut server_events); |
| |
| // Write an operation that is unknown. |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| vec![0x1f, 0x01, ase_id, 0xC0, 0xDE], |
| ); |
| |
| // Should have nothing to respond to |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected to still be pending, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 0xFF (Table 4.7) |
| // ASE_ID is 0x00, and Reason should be 0x00 |
| assert_eq!(cp_value, &[0x1f, 0xFF, 0x00, 0x01, 0x00]); |
| } |
| |
| #[test] |
| fn invalid_length() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| let ase_id = find_first_ase_id(&mut ascs_server, &fake_server, &mut server_events); |
| |
| // Write an operation that is the wrong length (too short) |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| vec![0x01, 0x01, ase_id, 0xC0, 0xDE], |
| ); |
| |
| // Should have nothing to respond to |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected to still be pending, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 0xFF (Table 4.7) |
| // ASE_ID is 0x00, and Reason should be 0x00 |
| assert_eq!(cp_value, &[0x01, 0xFF, 0x00, 0x02, 0x00]); |
| } |
| |
| #[track_caller] |
| fn assert_handle_value_eq( |
| ascs_server: &mut Pin<Box<AudioStreamControlServiceServer<FakeTypes>>>, |
| fake_server: &FakeServer, |
| events: &mut UnboundedReceiver<FakeServerEvent>, |
| handle: Handle, |
| expected: &[u8], |
| ) { |
| // Read the sink uuid |
| fake_server.incoming_read(PeerId(1), ASCS_SERVICE_ID, handle, 0); |
| // Poll the ascs server, should not result in an ASCS event |
| assert!(poll_server(ascs_server).is_pending()); |
| let sink_value; |
| match events.poll_next_unpin(&mut futures_test::task::noop_context()) { |
| Poll::Ready(Some(FakeServerEvent::ReadResponded { service_id, handle: _, value })) => { |
| assert_eq!(service_id, ASCS_SERVICE_ID); |
| sink_value = value.unwrap(); |
| } |
| x => panic!("Expected the read to be responded to got {x:?}"), |
| }; |
| |
| assert_eq!(sink_value, expected); |
| } |
| |
| #[rustfmt::skip] |
| fn build_codec_configure(ase_id: u8) -> Vec<u8> { |
| // Balanced Latency, 1m phy, LC3 codec (no company / company codecid): ltv (xx |
| // bytes), 48khz, frame_duration 10ms, stereo audio channels, |
| vec![ |
| 0x01, 0x01, ase_id, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, |
| 0x10, // 16 bytes |
| 0x02, 0x01, 0x08, // 48khz |
| 0x02, 0x02, 0x01, // 10ms duration |
| 0x05, 0x03, 0x00, 0x00, 0x00, 0x03, // FrontLeft + FrontRight |
| 0x03, 0x04, 0x64, 0x00, // 100 bytes per codec frame |
| ] |
| } |
| |
| #[test] |
| fn invalid_ase_ids() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| let ase_id = find_first_ase_id(&mut ascs_server, &fake_server, &mut server_events); |
| |
| // Try with an invalid ase_id |
| let codec_configure_bad_ase_id = build_codec_configure(0x00); |
| |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| codec_configure_bad_ase_id.clone(), |
| ); |
| |
| // Should have nothing to respond to |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected to still be pending, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match (0x01) |
| // Number_of_ASEs should be 1 |
| // ASE_ID is 0x00, and Reason should be 0x03 (invalid ASE ID) |
| assert_eq!(cp_value, &[0x01, 0x01, 0x00, 0x03, 0x00]); |
| |
| // Try with one valid and one invalid |
| let mut codec_configure_two_ase_ids_one_bad = build_codec_configure(ase_id); |
| // Adjust the number of ASEs |
| codec_configure_two_ase_ids_one_bad[1] = 0x02; |
| // This one has a bad ase_id in it |
| // TODO(b/518022833): adjust to find all ASE_IDs and randomly pick a non-used |
| // one) |
| let bad_ase_id_configure = build_codec_configure(ase_id | 0xF0); |
| // Skip the opcode and num of ase_ids in this one |
| codec_configure_two_ase_ids_one_bad.extend(&bad_ase_id_configure[2..]); |
| |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| codec_configure_two_ase_ids_one_bad.clone(), |
| ); |
| |
| // Should have the one valid CodecConfigure to repond to |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::CodecConfigure { |
| configuration: |
| CodecConfiguration { |
| target_latency, |
| target_phy, |
| codec_id, |
| codec_specific_configuration, |
| ase_id: configured_ase_id, |
| }, |
| responder, |
| }))) => { |
| use crate::types::*; |
| use bt_common::core::{CodecId, CodingFormat}; |
| assert_eq!(AseId(ase_id), configured_ase_id); |
| assert_eq!(target_latency, TargetLatency::TargetBalanced); |
| assert_eq!(target_phy, TargetPhy::Le1MPhy); |
| assert_eq!(codec_id, CodecId::Assigned(CodingFormat::Lc3)); |
| assert_eq!(codec_specific_configuration.len(), 16); |
| responder.accept( |
| Framing::Unframed, |
| vec![Phy::Le1MPhy], |
| 0x10, |
| MaxTransportLatency::try_from(std::time::Duration::from_millis(40)).unwrap(), |
| PresentationDelayRange::build(20000, 100000).unwrap(), |
| ); |
| } |
| x => panic!("Expected CodecConfigure, got {x:?}"), |
| } |
| // Then we should be pending. |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected to still be pending, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 2 |
| // ASE_ID should match and succeed, and the other ase_id should have failed. |
| assert_eq!(cp_value, &[0x01, 0x02, ase_id | 0xF0, 0x03, 0x00, ase_id, 0x00, 0x00]); |
| } |
| |
| #[test] |
| fn application_rejection() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| let ase_id = find_first_ase_id(&mut ascs_server, &fake_server, &mut server_events); |
| |
| // Codec configure |
| let codec_configure = build_codec_configure(ase_id); |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, codec_configure.clone()); |
| |
| // Should have a CodecConfigure to respond to. Let's reject. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::CodecConfigure { |
| configuration: |
| CodecConfiguration { |
| target_latency, |
| target_phy, |
| codec_id, |
| codec_specific_configuration, |
| ase_id, |
| }, |
| responder, |
| }))) => { |
| use crate::types::*; |
| use bt_common::core::{CodecId, CodingFormat}; |
| assert_eq!(target_latency, TargetLatency::TargetBalanced); |
| assert_eq!(target_phy, TargetPhy::Le1MPhy); |
| assert_eq!(codec_id, CodecId::Assigned(CodingFormat::Lc3)); |
| assert_eq!(codec_specific_configuration.len(), 16); |
| responder.reject(ResponseCode::InsufficientResources { ase_id }); |
| } |
| |
| x => panic!("Expected CodecConfigure, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and response should match. |
| assert_eq!(cp_value, &[0x01, 0x01, ase_id, 0x0D, 0x00]); |
| } |
| |
| #[test] |
| fn endpoint_lifecycle_source() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| let ase_id = find_first_ase_id(&mut ascs_server, &fake_server, &mut server_events); |
| |
| // QosConfigure the ase_id, with CIG 0x01, CIS 0x01, SDU Interval 0x1FF, |
| // Unframed, 1m PHY, Max SDU 0x0FFF, RetransmissionNumber 0xFF, |
| // Max_Transport_latency max (0x0FA0), and 30ms (0x007530) |
| let qos_configure = vec![ |
| 0x02, 0x01, ase_id, 0x01, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x01, 0xFF, 0x0F, 0xFF, 0xA0, 0x0F, |
| 0x30, 0x75, 0x00, |
| ]; |
| // Can't transition to QoS without CodecConfigure first |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, qos_configure.clone()); |
| |
| // Should have nothing to respond to |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected to still be pending, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 |
| // ASE_ID is 0x00, and Reason should be 0x04 (invalid state machine transition) |
| assert_eq!(cp_value, &[0x02, 0x01, ase_id, 0x04, 0x00]); |
| |
| // And the ASE should still be in idle. |
| assert_handle_value_eq( |
| &mut ascs_server, |
| &fake_server, |
| &mut server_events, |
| Handle(3), |
| &[ase_id, 0x00], |
| ); |
| |
| // Go to CodecConfigure |
| let codec_configure = build_codec_configure(ase_id); |
| |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, codec_configure.clone()); |
| |
| // Should have a CodecConfigure to respond to. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::CodecConfigure { |
| configuration: |
| CodecConfiguration { |
| target_latency, |
| target_phy, |
| codec_id, |
| codec_specific_configuration, |
| .. |
| }, |
| responder, |
| }))) => { |
| use crate::types::*; |
| use bt_common::core::{CodecId, CodingFormat}; |
| assert_eq!(target_latency, TargetLatency::TargetBalanced); |
| assert_eq!(target_phy, TargetPhy::Le1MPhy); |
| assert_eq!(codec_id, CodecId::Assigned(CodingFormat::Lc3)); |
| assert_eq!(codec_specific_configuration.len(), 16); |
| responder.accept( |
| Framing::Unframed, |
| vec![Phy::Le1MPhy], |
| 0x10, |
| MaxTransportLatency::try_from(std::time::Duration::from_millis(40)).unwrap(), |
| PresentationDelayRange::build(20000, 100000).unwrap(), |
| ); |
| } |
| |
| x => panic!("Expected CodecConfigure, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x01, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x01); // Configured state |
| assert_eq!(value.len(), 2 + 23 + usize::from(value[24])) |
| // state length should be 2 (static) + 24 |
| // (configured state) + len of codec |
| // configuration |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // QoSConfigure |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, qos_configure.clone()); |
| |
| // Should have a QosConfigure to respond to. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::QosConfigure { |
| peer_id, |
| target_configuration: _, |
| responder, |
| }))) => { |
| assert_eq!(peer_id, PeerId(1)); |
| responder.accept(); |
| } |
| |
| x => panic!("Expected QoSConfigure, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x02, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x02); // QoS Configured |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Enable |
| let enable = vec![0x03, 0x01, ase_id, 0x00]; |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, enable.clone()); |
| |
| // Should have an event o respond to. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::Enable { |
| ase_id: enabled_ase_id, responder, .. |
| }))) => { |
| assert_eq!(ase_id, enabled_ase_id.0); |
| responder.accept(); |
| } |
| |
| x => panic!("Expected Enable, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x03, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x03); // Enabling |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Receiver Start Ready -> Streaming |
| let receiver_start_ready = vec![0x04, 0x01, ase_id]; |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| receiver_start_ready.clone(), |
| ); |
| |
| // We auto-accept this, so we should not have a response, but do generate an |
| // event. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::Start { |
| peer_id: _, |
| ase_id: started_ase_id, |
| cis: _, |
| }))) => { |
| assert_eq!(ase_id, started_ase_id.0); |
| } |
| x => panic!("Expected Start, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x04, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x04); // Streaming |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Disable -> Disabling |
| let disable = vec![0x05, 0x01, ase_id]; |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, disable.clone()); |
| |
| // We auto-accept this, so we should not have a event. |
| // We also don't generate an event until Receiver Stop Ready |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected no event on Disable, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x05, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x05); // Disabling |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Receiver Stop Ready -> QoSConfigured |
| let receiver_stop_ready = vec![0x06, 0x01, ase_id]; |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| receiver_stop_ready.clone(), |
| ); |
| |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::Disable { |
| peer_id: _, |
| ase_id: disabled_ase_id, |
| cis: _, |
| responder, |
| }))) => { |
| assert_eq!(ase_id, disabled_ase_id.0); |
| responder.accept(); |
| } |
| x => panic!("Expected Disable, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x06, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x02); // QosConfigured |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Release -> Releasing |
| let release = vec![0x08, 0x01, ase_id]; |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, release.clone()); |
| |
| // We don't expect an event for this (disable has already happened |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected no event from release, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x08, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(3), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x06); // Releasing |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // TODO(b/518022833): Automated behaviors are not done yet |
| // TODO(b/518022833): Test Update Metadata |
| } |
| |
| #[test] |
| fn endpoint_lifecycle_sink() { |
| let (mut ascs_server, fake_server, mut server_events) = published_server(); |
| |
| // Handle 3 is the Sink ASE |
| let ase_id = find_ase_id(&mut ascs_server, &fake_server, &mut server_events, Handle(4)); |
| |
| // QosConfigure the ase_id, with CIG 0x01, CIS 0x01, SDU Interval 0x1FF, |
| // Unframed, 1m PHY, Max SDU 0x0FFF, RetransmissionNumber 0xFF, |
| // Max_Transport_latency max (0x0FA0), and 30ms (0x007530) |
| let qos_configure = vec![ |
| 0x02, 0x01, ase_id, 0x01, 0x01, 0x00, 0xFF, 0x01, 0x00, 0x01, 0xFF, 0x0F, 0xFF, 0xA0, 0x0F, |
| 0x30, 0x75, 0x00, |
| ]; |
| // Can't transition to QoS without CodecConfigure first |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, qos_configure.clone()); |
| |
| // Should have nothing to respond to |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected to still be pending, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 |
| // ASE_ID is 0x00, and Reason should be 0x04 (invalid state machine transition) |
| assert_eq!(cp_value, &[0x02, 0x01, ase_id, 0x04, 0x00]); |
| |
| // And the ASE should still be in idle. |
| assert_handle_value_eq( |
| &mut ascs_server, |
| &fake_server, |
| &mut server_events, |
| Handle(4), |
| &[ase_id, 0x00], |
| ); |
| |
| // Go to CodecConfigure |
| let codec_configure = build_codec_configure(ase_id); |
| |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, codec_configure.clone()); |
| |
| // Should have a CodecConfigure to respond to. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::CodecConfigure { |
| configuration: |
| CodecConfiguration { |
| target_latency, |
| target_phy, |
| codec_id, |
| codec_specific_configuration, |
| .. |
| }, |
| responder, |
| }))) => { |
| use crate::types::*; |
| use bt_common::core::{CodecId, CodingFormat}; |
| assert_eq!(target_latency, TargetLatency::TargetBalanced); |
| assert_eq!(target_phy, TargetPhy::Le1MPhy); |
| assert_eq!(codec_id, CodecId::Assigned(CodingFormat::Lc3)); |
| assert_eq!(codec_specific_configuration.len(), 16); |
| responder.accept( |
| Framing::Unframed, |
| vec![Phy::Le1MPhy], |
| 0x10, |
| MaxTransportLatency::try_from(std::time::Duration::from_millis(40)).unwrap(), |
| PresentationDelayRange::build(20000, 100000).unwrap(), |
| ); |
| } |
| |
| x => panic!("Expected CodecConfigure, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x01, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(4), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x01); // Configured state |
| assert_eq!(value.len(), 2 + 23 + usize::from(value[24])) |
| // state length should be 2 (static) + 24 |
| // (configured state) + len of codec |
| // configuration |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // QoSConfigure |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, qos_configure.clone()); |
| |
| // Should have a QosConfigure to respond to. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::QosConfigure { |
| peer_id, |
| target_configuration: _, |
| responder, |
| }))) => { |
| assert_eq!(peer_id, PeerId(1)); |
| responder.accept(); |
| } |
| |
| x => panic!("Expected QoSConfigure, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x02, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(4), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x02); // QoS Configured |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Enable |
| let enable = vec![0x03, 0x01, ase_id, 0x00]; |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, enable.clone()); |
| |
| // Should have an event to respond to. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::Enable { |
| ase_id: enabled_ase_id, responder, .. |
| }))) => { |
| assert_eq!(ase_id, enabled_ase_id.0); |
| responder.accept(); |
| } |
| |
| x => panic!("Expected Enable, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x03, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(4), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x03); // Enabling |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Receiver Start Ready should not be allowed from the client for a Sink ASE |
| let receiver_start_ready = vec![0x04, 0x01, ase_id]; |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| receiver_start_ready.clone(), |
| ); |
| |
| // Should not have an event for the error. |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected no event for invalid ReceiverStartReady, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x04, 0x01, ase_id, 0x05, 0x00]); |
| |
| // TODO: We don't do server-initiated ReceiverStart yet, so we can't proceed to |
| // streaming. |
| |
| // Disable -> QosConfigured |
| let disable = vec![0x05, 0x01, ase_id]; |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, disable.clone()); |
| |
| // On Sink ASE, we generate the Disable event when Disabling. |
| match poll_server(&mut ascs_server) { |
| Poll::Ready(Some(Ok(ServiceEvent::Disable { |
| peer_id: _, |
| ase_id: disabled_ase_id, |
| cis: _, |
| responder, |
| }))) => { |
| assert_eq!(ase_id, disabled_ase_id.0); |
| responder.accept(); |
| } |
| x => panic!("Expected Disable, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x05, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(4), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x02); // Automatically went to QoSConfigure |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // Receiver Stop Ready is not allowed for a Sink ASE |
| let receiver_stop_ready = vec![0x06, 0x01, ase_id]; |
| fake_server.incoming_write( |
| PeerId(1), |
| ASCS_SERVICE_ID, |
| Handle(1), |
| 0, |
| receiver_stop_ready.clone(), |
| ); |
| |
| assert!(poll_server(&mut ascs_server).is_pending()); |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and Failure for Invalid ASE |
| // State Transition (since we are already in QosCondfiguredj |
| assert_eq!(cp_value, &[0x06, 0x01, ase_id, 0x04, 0x00]); |
| |
| // Release -> Releasing |
| let release = vec![0x08, 0x01, ase_id]; |
| fake_server.incoming_write(PeerId(1), ASCS_SERVICE_ID, Handle(1), 0, release.clone()); |
| |
| // We don't expect an event for this (disable has already happened |
| match poll_server(&mut ascs_server) { |
| Poll::Pending => {} |
| x => panic!("Expected no event from release, got {x:?}"), |
| }; |
| |
| let cp_value = expect_control_point_notified(&mut ascs_server, &mut server_events); |
| // Opcode should match |
| // Number_of_ASEs should be 1 should match ASE_ID and success 0x00 |
| assert_eq!(cp_value, &[0x08, 0x01, ase_id, 0x00, 0x00]); |
| |
| // And the ASE should be configured and notified |
| match expect_service_event(&mut server_events) { |
| FakeServerEvent::Notified { handle, value, peers, .. } => { |
| assert!(peers.contains(&PeerId(1))); |
| assert_eq!(Handle(4), handle); |
| assert_eq!(value[0], ase_id); |
| assert_eq!(value[1], 0x06); // Releasing |
| } |
| x => panic!("Expected Endpoint Notification, got {x:?}"), |
| } |
| |
| // TODO: Automated behaviors are not done yet |
| // TODO: Test Update Metadata |
| } |