rust/bt-broadcast-assistant: Parse broadcast name from scan results

Store broadcast name metadata discovered during central scanning
into BroadcastSource records.

This enables assistant utilities and user interfaces to present
human-readable broadcast names to users when scanning for public
broadcast audio streams, directly matching Public Broadcast Profile
specifications.

Bug: b/366310724
Test: cargo test
Change-Id: I015f00552950d609f21e461f5da8fb85b6485919
Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/2960
diff --git a/rust/bt-broadcast-assistant/src/assistant.rs b/rust/bt-broadcast-assistant/src/assistant.rs
index cde253a..b41dcd1 100644
--- a/rust/bt-broadcast-assistant/src/assistant.rs
+++ b/rust/bt-broadcast-assistant/src/assistant.rs
@@ -216,6 +216,7 @@
             broadcast_id: None,
             periodic_advertising_interval: None,
             endpoint: None,
+            broadcast_name: None,
         };
 
         Ok(self
@@ -254,6 +255,7 @@
             broadcast_id: None,
             periodic_advertising_interval: None,
             endpoint: Some(endpoint),
+            broadcast_name: None,
         };
 
         Ok(self
@@ -308,6 +310,7 @@
                 broadcast_id: Some(bid1),
                 periodic_advertising_interval: None,
                 endpoint: None,
+                broadcast_name: None,
             }
         );
 
@@ -334,6 +337,7 @@
                     presentation_delay_ms: 32,
                     big: vec![]
                 }),
+                broadcast_name: None,
             }
         );
 
@@ -366,6 +370,7 @@
                 broadcast_id: Some(bid2),
                 periodic_advertising_interval: None,
                 endpoint: None,
+                broadcast_name: None,
             }
         );
 
diff --git a/rust/bt-broadcast-assistant/src/assistant/event.rs b/rust/bt-broadcast-assistant/src/assistant/event.rs
index 40c32bc..1780cf1 100644
--- a/rust/bt-broadcast-assistant/src/assistant/event.rs
+++ b/rust/bt-broadcast-assistant/src/assistant/event.rs
@@ -154,12 +154,19 @@
     ) -> Result<Option<BroadcastSource>, PacketError> {
         let mut source = None;
         for datum in &scan_result.advertised {
-            let AdvertisingDatum::ServiceData(uuid, data) = datum else {
-                continue;
-            };
-            if *uuid == BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE {
-                let bid = BroadcastId::decode(data.as_slice()).0?;
-                source.get_or_insert(BroadcastSource::default()).with_broadcast_id(bid);
+            match datum {
+                AdvertisingDatum::ServiceData(uuid, data)
+                    if *uuid == BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE =>
+                {
+                    let bid = BroadcastId::decode(data.as_slice()).0?;
+                    source.get_or_insert(BroadcastSource::default()).with_broadcast_id(bid);
+                }
+                AdvertisingDatum::BroadcastName(name) => {
+                    source
+                        .get_or_insert(BroadcastSource::default())
+                        .with_broadcast_name(name.clone());
+                }
+                _ => {}
             }
         }
         if let Some(src) = &mut source {
@@ -403,10 +410,13 @@
             id: broadcast_source_pid,
             connectable: true,
             name: PeerName::Unknown,
-            advertised: vec![AdvertisingDatum::ServiceData(
-                BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE,
-                vec![0x01, 0x02, 0x03],
-            )],
+            advertised: vec![
+                AdvertisingDatum::ServiceData(
+                    BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE,
+                    vec![0x01, 0x02, 0x03],
+                ),
+                AdvertisingDatum::BroadcastName("Test Broadcast".to_string()),
+            ],
             advertising_sid: Some(1),
             periodic_advertising_interval: Some(0x0100),
         }));
@@ -473,6 +483,7 @@
             assert_eq!(advertising_sid, AdvertisingSetId(1));
             assert_eq!(source.periodic_advertising_interval, Some(PeriodicAdvertisingInterval(0x0100)));
             assert_eq!(source.address, Some([1, 2, 3, 4, 5, 6]));
+            assert_eq!(source.broadcast_name, Some("Test Broadcast".to_string()));
         });
 
         // Verify that the PA sync was stopped (removed from active_syncs) to conserve
diff --git a/rust/bt-broadcast-assistant/src/types.rs b/rust/bt-broadcast-assistant/src/types.rs
index 93f9715..e6d77d4 100644
--- a/rust/bt-broadcast-assistant/src/types.rs
+++ b/rust/bt-broadcast-assistant/src/types.rs
@@ -20,6 +20,7 @@
     pub(crate) broadcast_id: Option<BroadcastId>,
     pub(crate) periodic_advertising_interval: Option<PeriodicAdvertisingInterval>,
     pub(crate) endpoint: Option<BroadcastAudioSourceEndpoint>,
+    pub(crate) broadcast_name: Option<String>,
 }
 
 impl BroadcastSource {
@@ -60,6 +61,11 @@
         self
     }
 
+    pub fn with_broadcast_name(&mut self, name: String) -> &mut Self {
+        self.broadcast_name = Some(name);
+        self
+    }
+
     /// Merge fields from other broadcast source into this broadcast source.
     /// Set fields in other source take priority over this source.
     /// If a field in the other broadcast source is none, it's ignored and
@@ -80,6 +86,9 @@
         if let Some(endpoint) = &other.endpoint {
             self.endpoint = Some(endpoint.clone());
         }
+        if let Some(broadcast_name) = &other.broadcast_name {
+            self.broadcast_name = Some(broadcast_name.clone());
+        }
     }
 
     /// Returns the representation of this object's endpoint field to