rust/bt-{gatt, broadcast-assistant}: Add broadcast name and PA interval
Support LE Audio broadcast metadata in central scanning and assistant
event records to enable broadcast source discovery.
Bug: b/366310724
Test: cargo test
Change-Id: I3c3c06b2df776464c37927816091d325cceaa326
Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/2920
diff --git a/rust/README.md b/rust/README.md
index dcd3d7e..4130001 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -10,6 +10,13 @@
async features of rust are executor-agnostic - using any async executor
should be possible.
+Additionally, any changes to the repository should also be formatted and
+checked for style, using the commands:
+```
+$ cargo fmt
+$ cargo check
+```
+
## Services
Crates that implement services as defined by the published specifications.
@@ -76,9 +83,9 @@
# Important Note about User Data and Privacy
-As Bluetooth is often used for data sharing and can send sensitive information,
-care should be taken by integrators of this library to ensure that user data and
-privacy is maintained.
+As Bluetooth is often used for data sharing and can send sensitive
+information, care should be taken by integrators of this library to ensure
+that user data and privacy is maintained.
The crates here do not store any data persistently, and identifiers are stable
only as long as the connection is maintained.
diff --git a/rust/bt-bass/src/client.rs b/rust/bt-bass/src/client.rs
index 7519501..8ad2ef6 100644
--- a/rust/bt-bass/src/client.rs
+++ b/rust/bt-bass/src/client.rs
@@ -14,7 +14,7 @@
use parking_lot::Mutex;
use bt_bap::types::BroadcastId;
-use bt_common::core::{AddressType, AdvertisingSetId, PaInterval};
+use bt_common::core::{AddressType, AdvertisingSetId, PeriodicAdvertisingInterval};
use bt_common::generic_audio::metadata_ltv::Metadata;
use bt_common::packet_encoding::Decodable;
use bt_gatt::client::{CharacteristicNotification, PeerService, ServiceCharacteristic};
@@ -270,7 +270,7 @@
advertiser_address: [u8; ADDRESS_BYTE_SIZE],
sid: AdvertisingSetId,
pa_sync: PaSync,
- pa_interval: PaInterval,
+ pa_interval: PeriodicAdvertisingInterval,
subgroups: Vec<BigSubgroup>,
) -> Result<(), Error> {
let op = AddSourceOperation::new(
@@ -304,7 +304,7 @@
&self,
broadcast_id: BroadcastId,
pa_sync: PaSync,
- pa_interval: Option<PaInterval>,
+ pa_interval: Option<PeriodicAdvertisingInterval>,
bis_sync: Option<HashMap<SubgroupIndex, BisSync>>,
metadata_map: Option<HashMap<SubgroupIndex, Vec<Metadata>>>,
) -> Result<(), Error> {
@@ -347,7 +347,7 @@
ModifySourceOperation::new(
state.source_id,
pa_sync,
- pa_interval.unwrap_or(PaInterval::unknown()),
+ pa_interval.unwrap_or(PeriodicAdvertisingInterval::unknown()),
state.subgroups,
)
};
@@ -737,7 +737,7 @@
[0x04, 0x10, 0x00, 0x00, 0x00, 0x00],
AdvertisingSetId(1),
PaSync::DoNotSync,
- PaInterval::unknown(),
+ PeriodicAdvertisingInterval::unknown(),
vec![],
);
pin_mut!(op_fut);
@@ -778,7 +778,7 @@
let op_fut = client.modify_broadcast_source(
BroadcastId::try_from(0x11).unwrap(),
PaSync::DoNotSync,
- Some(PaInterval(0xAAAA)),
+ Some(PeriodicAdvertisingInterval(0xAAAA)),
None,
None,
);
diff --git a/rust/bt-bass/src/types.rs b/rust/bt-bass/src/types.rs
index 70c97ad..e8db40e 100644
--- a/rust/bt-bass/src/types.rs
+++ b/rust/bt-bass/src/types.rs
@@ -4,7 +4,7 @@
use bt_bap::types::BroadcastId;
use bt_common::core::ltv::LtValue;
-use bt_common::core::{AddressType, AdvertisingSetId, PaInterval};
+use bt_common::core::{AddressType, AdvertisingSetId, PeriodicAdvertisingInterval};
use bt_common::generic_audio::metadata_ltv::*;
use bt_common::packet_encoding::{Decodable, Encodable, Error as PacketError};
use bt_common::{decodable_enum, Uuid};
@@ -155,7 +155,7 @@
pub(crate) advertising_sid: AdvertisingSetId,
pub(crate) broadcast_id: BroadcastId,
pub(crate) pa_sync: PaSync,
- pub(crate) pa_interval: PaInterval,
+ pub(crate) pa_interval: PeriodicAdvertisingInterval,
pub(crate) subgroups: Vec<BigSubgroup>,
}
@@ -166,7 +166,7 @@
+ AdvertisingSetId::BYTE_SIZE
+ BroadcastId::BYTE_SIZE
+ PA_SYNC_BYTE_SIZE
- + PaInterval::BYTE_SIZE
+ + PeriodicAdvertisingInterval::BYTE_SIZE
+ NUM_SUBGROUPS_BYTE_SIZE;
pub fn new(
@@ -175,7 +175,7 @@
advertising_sid: AdvertisingSetId,
broadcast_id: BroadcastId,
pa_sync: PaSync,
- pa_interval: PaInterval,
+ pa_interval: PeriodicAdvertisingInterval,
subgroups: Vec<BigSubgroup>,
) -> Self {
AddSourceOperation {
@@ -212,7 +212,8 @@
let advertising_sid = AdvertisingSetId(buf[8]);
let broadcast_id = BroadcastId::decode(&buf[9..12]).0?;
let pa_sync = PaSync::try_from(buf[12])?;
- let pa_interval = PaInterval(u16::from_le_bytes(buf[13..15].try_into().unwrap()));
+ let pa_interval =
+ PeriodicAdvertisingInterval(u16::from_le_bytes(buf[13..15].try_into().unwrap()));
let num_subgroups = buf[15] as usize;
let mut subgroups = Vec::new();
@@ -284,7 +285,7 @@
pub struct ModifySourceOperation {
source_id: SourceId,
pa_sync: PaSync,
- pa_interval: PaInterval,
+ pa_interval: PeriodicAdvertisingInterval,
subgroups: Vec<BigSubgroup>,
}
@@ -292,13 +293,13 @@
const MIN_PACKET_SIZE: usize = ControlPointOpcode::BYTE_SIZE
+ SOURCE_ID_BYTE_SIZE
+ PA_SYNC_BYTE_SIZE
- + PaInterval::BYTE_SIZE
+ + PeriodicAdvertisingInterval::BYTE_SIZE
+ NUM_SUBGROUPS_BYTE_SIZE;
pub fn new(
source_id: SourceId,
pa_sync: PaSync,
- pa_interval: PaInterval,
+ pa_interval: PeriodicAdvertisingInterval,
subgroups: Vec<BigSubgroup>,
) -> Self {
ModifySourceOperation { source_id, pa_sync, pa_interval, subgroups }
@@ -323,7 +324,8 @@
let _ = Self::check_opcode(buf[0])?;
let source_id = buf[1];
let pa_sync = PaSync::try_from(buf[2])?;
- let pa_interval = PaInterval(u16::from_le_bytes(buf[3..5].try_into().unwrap()));
+ let pa_interval =
+ PeriodicAdvertisingInterval(u16::from_le_bytes(buf[3..5].try_into().unwrap()));
let num_subgroups = buf[5] as usize;
let mut subgroups = Vec::new();
@@ -1154,7 +1156,7 @@
AdvertisingSetId(1),
BroadcastId::try_from(0x11).unwrap(),
PaSync::DoNotSync,
- PaInterval::unknown(),
+ PeriodicAdvertisingInterval::unknown(),
vec![],
);
assert_eq!(op.encoded_len(), 16);
@@ -1186,7 +1188,7 @@
AdvertisingSetId(1),
BroadcastId::try_from(0x11).unwrap(),
PaSync::SyncPastAvailable,
- PaInterval::unknown(),
+ PeriodicAdvertisingInterval::unknown(),
subgroups,
);
assert_eq!(op.encoded_len(), 31); // 16 for minimum params and params 15 for the subgroup.
@@ -1210,8 +1212,12 @@
#[test]
fn modify_source_without_subgroups() {
// Encoding operation with no subgroups.
- let op =
- ModifySourceOperation::new(0x0A, PaSync::SyncPastAvailable, PaInterval(0x1004), vec![]);
+ let op = ModifySourceOperation::new(
+ 0x0A,
+ PaSync::SyncPastAvailable,
+ PeriodicAdvertisingInterval(0x1004),
+ vec![],
+ );
assert_eq!(op.encoded_len(), 6);
let mut buf = vec![0u8; op.encoded_len()];
op.encode(&mut buf[..]).expect("shoud succeed");
@@ -1233,8 +1239,12 @@
BigSubgroup::new(Some(BisSync(0x000000FE)))
.with_metadata(vec![Metadata::BroadcastAudioImmediateRenderingFlag]), /* encoded_len = 7 */
];
- let op =
- ModifySourceOperation::new(0x0B, PaSync::DoNotSync, PaInterval::unknown(), subgroups);
+ let op = ModifySourceOperation::new(
+ 0x0B,
+ PaSync::DoNotSync,
+ PeriodicAdvertisingInterval::unknown(),
+ subgroups,
+ );
assert_eq!(op.encoded_len(), 21); // 6 for minimum params and params 15 for two subgroups.
let mut buf = vec![0u8; op.encoded_len()];
op.encode(&mut buf[..]).expect("shoud succeed");
diff --git a/rust/bt-broadcast-assistant/src/assistant.rs b/rust/bt-broadcast-assistant/src/assistant.rs
index b8b50af..495ed85 100644
--- a/rust/bt-broadcast-assistant/src/assistant.rs
+++ b/rust/bt-broadcast-assistant/src/assistant.rs
@@ -196,7 +196,7 @@
address_type: Some(address_type),
advertising_sid: Some(advertising_sid),
broadcast_id: None,
- pa_interval: None,
+ periodic_advertising_interval: None,
endpoint: None,
};
@@ -231,7 +231,7 @@
address_type: None,
advertising_sid: None,
broadcast_id: None,
- pa_interval: None,
+ periodic_advertising_interval: None,
endpoint: Some(endpoint),
};
@@ -284,7 +284,7 @@
address_type: Some(AddressType::Public),
advertising_sid: Some(AdvertisingSetId(1)),
broadcast_id: Some(bid),
- pa_interval: None,
+ periodic_advertising_interval: None,
endpoint: None,
}
);
@@ -303,7 +303,7 @@
address_type: Some(AddressType::Random),
advertising_sid: Some(AdvertisingSetId(1)),
broadcast_id: Some(bid),
- pa_interval: None,
+ periodic_advertising_interval: None,
endpoint: Some(BroadcastAudioSourceEndpoint {
presentation_delay_ms: 32,
big: vec![]
diff --git a/rust/bt-broadcast-assistant/src/assistant/event.rs b/rust/bt-broadcast-assistant/src/assistant/event.rs
index 741c04c..d9a535a 100644
--- a/rust/bt-broadcast-assistant/src/assistant/event.rs
+++ b/rust/bt-broadcast-assistant/src/assistant/event.rs
@@ -9,7 +9,7 @@
use std::task::Poll;
use bt_bap::types::{BroadcastAudioSourceEndpoint, BroadcastId};
-use bt_common::core::AdvertisingSetId;
+use bt_common::core::{AdvertisingSetId, PeriodicAdvertisingInterval};
use bt_common::packet_encoding::Decodable;
use bt_common::packet_encoding::Error as PacketError;
use bt_common::PeerId;
@@ -73,7 +73,9 @@
}
}
if let Some(src) = &mut source {
- src.advertising_sid = Some(AdvertisingSetId(scan_result.advertising_sid));
+ src.advertising_sid = scan_result.advertising_sid.map(AdvertisingSetId);
+ src.periodic_advertising_interval =
+ scan_result.periodic_advertising_interval.map(PeriodicAdvertisingInterval);
}
Ok(source)
}
@@ -185,7 +187,8 @@
BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE,
vec![0x01, 0x02, 0x03],
)],
- advertising_sid: 0,
+ advertising_sid: Some(0),
+ periodic_advertising_interval: None,
}));
// Found broadcast source event shouldn't have been sent since braodcast source
@@ -230,7 +233,8 @@
BASIC_AUDIO_ANNOUNCEMENT_SERVICE,
base_data.clone(),
)],
- advertising_sid: 1,
+ advertising_sid: Some(1),
+ periodic_advertising_interval: Some(0x0100),
}));
// Expect the stream to send out broadcast source found event since information
@@ -241,6 +245,7 @@
assert_matches!(event, Event::FoundBroadcastSource{peer, source} => {
assert_eq!(peer, broadcast_source_pid);
assert_eq!(source.advertising_sid, Some(AdvertisingSetId(1)));
+ assert_eq!(source.periodic_advertising_interval, Some(PeriodicAdvertisingInterval(0x0100)));
});
assert!(stream.poll_next_unpin(&mut noop_cx).is_pending());
@@ -254,7 +259,8 @@
BASIC_AUDIO_ANNOUNCEMENT_SERVICE,
base_data.clone(),
)],
- advertising_sid: 1,
+ advertising_sid: Some(1),
+ periodic_advertising_interval: Some(0x0100),
}));
// Shouldn't have gotten the event again since the information remained the
diff --git a/rust/bt-broadcast-assistant/src/assistant/peer.rs b/rust/bt-broadcast-assistant/src/assistant/peer.rs
index 73029d8..30be3f1 100644
--- a/rust/bt-broadcast-assistant/src/assistant/peer.rs
+++ b/rust/bt-broadcast-assistant/src/assistant/peer.rs
@@ -15,7 +15,7 @@
#[cfg(any(test, feature = "debug"))]
use bt_bass::types::BroadcastReceiveState;
use bt_bass::types::{BisSync, PaSync};
-use bt_common::core::PaInterval;
+use bt_common::core::PeriodicAdvertisingInterval;
use bt_common::packet_encoding::Error as PacketError;
use bt_common::PeerId;
#[cfg(any(test, feature = "debug"))]
@@ -129,7 +129,9 @@
broadcast_source.address.unwrap(),
broadcast_source.advertising_sid.unwrap(),
pa_sync,
- broadcast_source.pa_interval.unwrap_or(PaInterval::unknown()),
+ broadcast_source
+ .periodic_advertising_interval
+ .unwrap_or(PeriodicAdvertisingInterval::unknown()),
broadcast_source.endpoint_to_big_subgroups(bis_sync).map_err(Error::PacketError)?,
)
.await
@@ -154,7 +156,7 @@
let pa_interval = self
.broadcast_sources
.get_by_broadcast_id(&broadcast_id)
- .map(|bs| bs.pa_interval)
+ .map(|bs| bs.periodic_advertising_interval)
.unwrap_or(None);
self.bass
diff --git a/rust/bt-broadcast-assistant/src/types.rs b/rust/bt-broadcast-assistant/src/types.rs
index 2cd8d96..e1b6312 100644
--- a/rust/bt-broadcast-assistant/src/types.rs
+++ b/rust/bt-broadcast-assistant/src/types.rs
@@ -5,7 +5,7 @@
use bt_bap::types::*;
use bt_bass::types::{BigSubgroup, BisSync};
use bt_common::core::{Address, AddressType};
-use bt_common::core::{AdvertisingSetId, PaInterval};
+use bt_common::core::{AdvertisingSetId, PeriodicAdvertisingInterval};
use bt_common::packet_encoding::Error as PacketError;
use std::collections::HashMap;
@@ -19,7 +19,7 @@
pub(crate) address_type: Option<AddressType>,
pub(crate) advertising_sid: Option<AdvertisingSetId>,
pub(crate) broadcast_id: Option<BroadcastId>,
- pub(crate) pa_interval: Option<PaInterval>,
+ pub(crate) periodic_advertising_interval: Option<PeriodicAdvertisingInterval>,
pub(crate) endpoint: Option<BroadcastAudioSourceEndpoint>,
}
@@ -53,6 +53,14 @@
self
}
+ pub fn with_periodic_advertising_interval(
+ &mut self,
+ interval: PeriodicAdvertisingInterval,
+ ) -> &mut Self {
+ self.periodic_advertising_interval = Some(interval);
+ self
+ }
+
pub fn with_endpoint(&mut self, endpoint: BroadcastAudioSourceEndpoint) -> &mut Self {
self.endpoint = Some(endpoint);
self
@@ -75,8 +83,8 @@
if let Some(broadcast_id) = other.broadcast_id {
self.broadcast_id = Some(broadcast_id);
}
- if let Some(pa_interval) = other.pa_interval {
- self.pa_interval = Some(pa_interval);
+ if let Some(pa_interval) = other.periodic_advertising_interval {
+ self.periodic_advertising_interval = Some(pa_interval);
}
if let Some(endpoint) = &other.endpoint {
self.endpoint = Some(endpoint.clone());
diff --git a/rust/bt-common/src/core.rs b/rust/bt-common/src/core.rs
index fee3dc7..9fab7e3 100644
--- a/rust/bt-common/src/core.rs
+++ b/rust/bt-common/src/core.rs
@@ -60,9 +60,9 @@
/// SyncInfo Interval value which is 2 bytes long.
#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct PaInterval(pub u16);
+pub struct PeriodicAdvertisingInterval(pub u16);
-impl PaInterval {
+impl PeriodicAdvertisingInterval {
pub const BYTE_SIZE: usize = 2;
pub const UNKNOWN_VALUE: u16 = 0xFFFF;
@@ -71,7 +71,7 @@
}
}
-impl Encodable for PaInterval {
+impl Encodable for PeriodicAdvertisingInterval {
type Error = PacketError;
/// Encodees the PaInterval to 2 byte value using little endian encoding.
@@ -244,8 +244,8 @@
#[test]
fn encode_pa_interval() {
- let mut buf = [0; PaInterval::BYTE_SIZE];
- let interval = PaInterval(0x1004);
+ let mut buf = [0; PeriodicAdvertisingInterval::BYTE_SIZE];
+ let interval = PeriodicAdvertisingInterval(0x1004);
interval.encode(&mut buf[..]).expect("should succeed");
assert_eq!(buf, [0x04, 0x10]);
@@ -254,7 +254,7 @@
#[test]
fn encode_pa_interval_fails() {
let mut buf = [0; 1]; // Not enough buffer space.
- let interval = PaInterval(0x1004);
+ let interval = PeriodicAdvertisingInterval(0x1004);
interval.encode(&mut buf[..]).expect_err("should fail");
}
diff --git a/rust/bt-gatt/src/central.rs b/rust/bt-gatt/src/central.rs
index c896717..af55cd0 100644
--- a/rust/bt-gatt/src/central.rs
+++ b/rust/bt-gatt/src/central.rs
@@ -20,6 +20,7 @@
Appearance(u16),
TxPowerLevel(i8),
Uri(String),
+ BroadcastName(String),
}
/// Matches a single advertised attribute or condition from a Bluetooth Low
@@ -75,7 +76,8 @@
pub connectable: bool,
pub name: PeerName,
pub advertised: Vec<AdvertisingDatum>,
- pub advertising_sid: u8,
+ pub advertising_sid: Option<u8>,
+ pub periodic_advertising_interval: Option<u16>,
}
pub trait Central<T: crate::GattTypes> {
diff --git a/rust/bt-gatt/src/tests.rs b/rust/bt-gatt/src/tests.rs
index cc51dd5..6cab78b 100644
--- a/rust/bt-gatt/src/tests.rs
+++ b/rust/bt-gatt/src/tests.rs
@@ -243,7 +243,8 @@
connectable: true,
name: PeerName::CompleteName("Marie's Pixel 7 Pro".to_owned()),
advertised: vec![AdvertisingDatum::Services(vec![Uuid::from_u16(0x1844)])],
- advertising_sid: 0,
+ advertising_sid: Some(0),
+ periodic_advertising_interval: None,
};
let _ = scan_results.set_scanned_result(Ok(scanned_result));