blob: 1e779b1f4cdb3a0ec64ef2dfbdd36eb381687f31 [file] [edit]
// 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
}