| // Copyright 2026 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. |
| |
| #![allow(dead_code)] |
| |
| use bt_ascs::client::{ASE_CONTROL_POINT_UUID, SINK_ASE_UUID, SOURCE_ASE_UUID}; |
| use bt_ascs::server::ASCS_UUID; |
| use bt_ascs::types::{ |
| AseAdditionalParameters, AseId, AseState, AudioDirection, AudioStreamEndpoint, |
| }; |
| use bt_common::core::{CodecId, CodingFormat}; |
| use bt_common::packet_encoding::Encodable; |
| use bt_gatt::client::FromCharacteristic; |
| use bt_gatt::test_utils::{FakeClient, FakePeerService}; |
| use bt_gatt::types::{ |
| AttributePermissions, Characteristic, CharacteristicProperties, CharacteristicProperty, Handle, |
| }; |
| use bt_pacs::{PacRecord, SourcePac, PACS_UUID}; |
| |
| pub const SOURCE_PAC_HANDLE: Handle = Handle(1); |
| pub const SINK_ASE_HANDLE: Handle = Handle(2); |
| pub const CONTROL_POINT_HANDLE: Handle = Handle(3); |
| pub const SOURCE_ASE_HANDLE: Handle = Handle(4); |
| |
| pub const DEFAULT_SINK_ASE_ID: AseId = AseId(1); |
| pub const DEFAULT_SOURCE_ASE_ID: AseId = AseId(2); |
| |
| /// Creates a `FakePeerService` configured as a PACS service with a single |
| /// Source PAC record for LC3. |
| pub fn create_pacs_service() -> FakePeerService { |
| let mut pacs_service = FakePeerService::new(); |
| let pac_record = PacRecord { |
| codec_id: CodecId::Assigned(CodingFormat::Lc3), |
| codec_specific_capabilities: vec![], |
| metadata: vec![], |
| }; |
| |
| let mut pac_buf = vec![0; 1 + pac_record.encoded_len()]; |
| pac_buf[0] = 1; // 1 record |
| pac_record.encode(&mut pac_buf[1..]).unwrap(); |
| |
| let source_pac_chr = Characteristic { |
| handle: SOURCE_PAC_HANDLE, |
| uuid: SourcePac::UUID, |
| properties: CharacteristicProperty::Read.into(), |
| permissions: AttributePermissions::default(), |
| descriptors: vec![], |
| }; |
| pacs_service.add_characteristic(source_pac_chr, pac_buf); |
| pacs_service |
| } |
| |
| /// Sets up a PACS service in `client` with a single Source PAC record for LC3. |
| /// Adds the service to `client` and returns the `FakePeerService`. |
| pub fn setup_pacs_service(client: &mut FakeClient) -> FakePeerService { |
| let pacs_service = create_pacs_service(); |
| client.add_service(PACS_UUID, true, pacs_service.clone()); |
| pacs_service |
| } |
| |
| /// Creates a `FakePeerService` configured as an ASCS service with |
| /// `sink_ase_count` sink ASEs, `source_ase_count` source ASEs, and a Control |
| /// Point initialized with `control_point_val`. |
| pub fn create_ascs_service( |
| sink_ase_count: usize, |
| source_ase_count: usize, |
| control_point_val: Vec<u8>, |
| ) -> FakePeerService { |
| let mut ascs_service = FakePeerService::new(); |
| |
| let control_point_chr = Characteristic { |
| handle: CONTROL_POINT_HANDLE, |
| uuid: ASE_CONTROL_POINT_UUID, |
| properties: CharacteristicProperties::WRITE_NOTIFY, |
| permissions: AttributePermissions::default(), |
| descriptors: vec![], |
| }; |
| ascs_service.add_characteristic(control_point_chr, control_point_val); |
| |
| // ASE ids and handles are allocated from iterators shared by every endpoint |
| // characteristic, so sinks and sources can never collide with each other. |
| // The handle reserved for the control point characteristic is skipped. |
| let mut ase_ids = (1..).map(AseId); |
| let mut handles = (SINK_ASE_HANDLE.0..).map(Handle).filter(|h| *h != CONTROL_POINT_HANDLE); |
| |
| let directions = std::iter::repeat_n(AudioDirection::Sink, sink_ase_count) |
| .chain(std::iter::repeat_n(AudioDirection::Source, source_ase_count)); |
| |
| for direction in directions { |
| let endpoint = AudioStreamEndpoint { |
| handle: Handle(0), |
| direction, |
| ase_id: ase_ids.next().expect("ase ids available"), |
| state: AseState::Idle, |
| additional: AseAdditionalParameters::None, |
| }; |
| let mut ase_buf = vec![0; endpoint.encoded_len()]; |
| endpoint.encode(&mut ase_buf).unwrap(); |
| |
| let uuid = match direction { |
| AudioDirection::Sink => SINK_ASE_UUID, |
| AudioDirection::Source => SOURCE_ASE_UUID, |
| }; |
| let ase_chr = Characteristic { |
| handle: handles.next().expect("handles available"), |
| uuid, |
| properties: CharacteristicProperties::READ_NOTIFY, |
| permissions: AttributePermissions::default(), |
| descriptors: vec![], |
| }; |
| ascs_service.add_characteristic(ase_chr, ase_buf); |
| } |
| |
| ascs_service |
| } |
| |
| /// Sets up an ASCS service in `client` with `sink_ase_count` sink ASEs, |
| /// `source_ase_count` source ASEs, and an empty Control Point. Adds the service |
| /// to `client` and returns the `FakePeerService`. |
| pub fn setup_ascs_service( |
| client: &mut FakeClient, |
| sink_ase_count: usize, |
| source_ase_count: usize, |
| ) -> FakePeerService { |
| setup_ascs_service_with_cp_value(client, sink_ase_count, source_ase_count, vec![]) |
| } |
| |
| /// Sets up an ASCS service in `client` with `sink_ase_count` sink ASEs, |
| /// `source_ase_count` source ASEs, and a Control Point initialized with |
| /// `control_point_val`. Adds the service to `client` and returns the |
| /// `FakePeerService`. |
| pub fn setup_ascs_service_with_cp_value( |
| client: &mut FakeClient, |
| sink_ase_count: usize, |
| source_ase_count: usize, |
| control_point_val: Vec<u8>, |
| ) -> FakePeerService { |
| let ascs_service = create_ascs_service(sink_ase_count, source_ase_count, control_point_val); |
| client.add_service(ASCS_UUID, true, ascs_service.clone()); |
| ascs_service |
| } |