blob: 282eac2ada3483a220f602f5b0f22409ab3b9e09 [file] [log] [blame]
// Copyright 2022 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.
//! Service Discovery Protocol (SDP) record definitions for the Hands-Free
//! and Audio Gateway roles.
use bitflags::bitflags;
use fidl_fuchsia_bluetooth_bredr as bredr;
use fuchsia_bluetooth::types::Uuid;
use crate::config::HandsFreeFeatureSupport;
/// SDP Attribute ID for the Supported Features of HFP.
/// Defined in Assigned Numbers for SDP
/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
const ATTR_ID_HFP_SUPPORTED_FEATURES: u16 = 0x0311;
/// Major Version of HFP implementation
const PROFILE_MAJOR_VERSION: u8 = 1;
/// Minor Version of HFP implementation
const PROFILE_MINOR_VERSION: u8 = 8;
// The inner mod is required because bitflags cannot pass the attribute through to the single
// variant, and attributes cannot be applied to macro invocations.
mod inner_attr {
// Part of the code for the DEFAULT case that's produced by the macro triggers the lint, but as
// a whole, the produced code is still correct.
#![allow(clippy::bad_bit_mask)] // TODO(b/303500202) Remove once addressed in bitflags.
use super::bitflags;
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(super) struct HandsFreeFeaturesSdpAttribute: u16 {
const ECHO_CANCELLATION_AND_NOISE_REDUCTION = 0b0000_0001;
const CALL_WAITING_OR_THREE_WAY_CALLING = 0b0000_0010;
const CLI_PRESENTATION_CAPABILITY = 0b0000_0100;
const VOICE_RECOGNITION_ACTIVATION = 0b0000_1000;
const REMOTE_VOLUME_CONTROL = 0b0001_0000;
const WIDEBAND_SPEECH = 0b0010_0000;
const ENHANCED_VOICE_RECOGNITION = 0b0100_0000;
const EHANCED_VOICE_RECOGNITION_TEXT = 0b1000_0000;
/// Defined by HFP v1.8, Table 5.1: Service Record for the HF
const DEFAULT = 0b0000_0000;
}
}
}
use inner_attr::HandsFreeFeaturesSdpAttribute;
impl From<HandsFreeFeatureSupport> for HandsFreeFeaturesSdpAttribute {
fn from(features: HandsFreeFeatureSupport) -> Self {
let mut value = Self::empty();
value.set(Self::ECHO_CANCELLATION_AND_NOISE_REDUCTION, features.ec_or_nr);
value.set(
Self::CALL_WAITING_OR_THREE_WAY_CALLING,
features.call_waiting_or_three_way_calling,
);
value.set(Self::CLI_PRESENTATION_CAPABILITY, features.cli_presentation_capability);
value.set(Self::VOICE_RECOGNITION_ACTIVATION, features.voice_recognition_activation);
value.set(Self::REMOTE_VOLUME_CONTROL, features.remote_volume_control);
value.set(Self::WIDEBAND_SPEECH, features.wide_band_speech);
value.set(Self::ENHANCED_VOICE_RECOGNITION, features.enhanced_voice_recognition);
value.set(
Self::EHANCED_VOICE_RECOGNITION_TEXT,
features.enhanced_voice_recognition_with_text,
);
value
}
}
/// Makes the SDP definition for the HFP Hands Free service
/// See HFP v1.8, Table 5.1
pub fn hands_free(features: HandsFreeFeatureSupport) -> bredr::ServiceDefinition {
let supported_features = HandsFreeFeaturesSdpAttribute::from(features).bits();
bredr::ServiceDefinition {
service_class_uuids: Some(vec![
Uuid::new16(bredr::ServiceClassProfileIdentifier::Handsfree.into_primitive()).into(),
Uuid::new16(bredr::ServiceClassProfileIdentifier::GenericAudio.into_primitive()).into(),
]),
protocol_descriptor_list: Some(vec![
bredr::ProtocolDescriptor {
protocol: Some(bredr::ProtocolIdentifier::L2Cap),
params: Some(vec![]),
..Default::default()
},
bredr::ProtocolDescriptor {
protocol: Some(bredr::ProtocolIdentifier::Rfcomm),
params: Some(vec![]),
..Default::default()
},
]),
profile_descriptors: Some(vec![bredr::ProfileDescriptor {
profile_id: Some(bredr::ServiceClassProfileIdentifier::Handsfree),
major_version: Some(PROFILE_MAJOR_VERSION),
minor_version: Some(PROFILE_MINOR_VERSION),
..Default::default()
}]),
additional_attributes: Some(vec![bredr::Attribute {
id: Some(ATTR_ID_HFP_SUPPORTED_FEATURES),
element: Some(bredr::DataElement::Uint16(supported_features)),
..Default::default()
}]),
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Make sure all the flags are correctly mapped in the 'From' implementation.
/// Bit flags are independent so a test covering each flag independently is sufficient
#[fuchsia::test]
fn hf_features_sdp_attr_from_hf_features() {
let ec_nr = HandsFreeFeatureSupport { ec_or_nr: true, ..Default::default() };
assert_eq!(
HandsFreeFeaturesSdpAttribute::ECHO_CANCELLATION_AND_NOISE_REDUCTION,
ec_nr.into()
);
let call_waiting_or_three_way_calling = HandsFreeFeatureSupport {
call_waiting_or_three_way_calling: true,
..Default::default()
};
assert_eq!(
HandsFreeFeaturesSdpAttribute::CALL_WAITING_OR_THREE_WAY_CALLING,
call_waiting_or_three_way_calling.into()
);
let cli_presentation_capability =
HandsFreeFeatureSupport { cli_presentation_capability: true, ..Default::default() };
assert_eq!(
HandsFreeFeaturesSdpAttribute::CLI_PRESENTATION_CAPABILITY,
cli_presentation_capability.into()
);
let voice_recognition_activation =
HandsFreeFeatureSupport { voice_recognition_activation: true, ..Default::default() };
assert_eq!(
HandsFreeFeaturesSdpAttribute::VOICE_RECOGNITION_ACTIVATION,
voice_recognition_activation.into()
);
let remote_volume_control =
HandsFreeFeatureSupport { remote_volume_control: true, ..Default::default() };
assert_eq!(
HandsFreeFeaturesSdpAttribute::REMOTE_VOLUME_CONTROL,
remote_volume_control.into()
);
let wide_band_speech =
HandsFreeFeatureSupport { wide_band_speech: true, ..Default::default() };
assert_eq!(HandsFreeFeaturesSdpAttribute::WIDEBAND_SPEECH, wide_band_speech.into());
let enhanced_voice_recognition =
HandsFreeFeatureSupport { enhanced_voice_recognition: true, ..Default::default() };
assert_eq!(
HandsFreeFeaturesSdpAttribute::ENHANCED_VOICE_RECOGNITION,
enhanced_voice_recognition.into()
);
let enhanced_voice_recognition_with_text = HandsFreeFeatureSupport {
enhanced_voice_recognition_with_text: true,
..Default::default()
};
assert_eq!(
HandsFreeFeaturesSdpAttribute::EHANCED_VOICE_RECOGNITION_TEXT,
enhanced_voice_recognition_with_text.into()
);
}
}