fuchsia_bluetooth/
profile.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_bluetooth as fidl_bt;
6use fidl_fuchsia_bluetooth_bredr::{
7    self as fidl_bredr, ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, ATTR_SERVICE_CLASS_ID_LIST,
8    ProfileDescriptor,
9};
10use fidl_table_validation::ValidFidlTable;
11#[cfg(target_os = "fuchsia")]
12use fuchsia_inspect as inspect;
13#[cfg(target_os = "fuchsia")]
14use fuchsia_inspect_derive::{AttachError, Inspect, Unit};
15use std::cmp::min;
16use std::collections::HashSet;
17
18use crate::assigned_numbers::AssignedNumber;
19use crate::assigned_numbers::constants::SERVICE_CLASS_UUIDS;
20use crate::error::Error;
21use crate::types::Uuid;
22
23/// BR/EDR types for the AVRCP profile.
24pub mod avrcp;
25
26/// The Protocol and Service Multiplexer (PSM) for L2cap connections.
27#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
28pub struct Psm(u16);
29
30impl Psm {
31    /// PSMs commonly used in the codebase.
32    pub const RFCOMM: Self = Self(fidl_bredr::PSM_RFCOMM);
33    pub const HID_CONTROL: Self = Self(fidl_bredr::PSM_HID_CONTROL);
34    pub const HID_INTERRUPT: Self = Self(fidl_bredr::PSM_HID_INTERRUPT);
35    pub const AVDTP: Self = Self(fidl_bredr::PSM_AVDTP);
36    pub const AVCTP: Self = Self(fidl_bredr::PSM_AVCTP);
37    pub const AVCTP_BROWSE: Self = Self(fidl_bredr::PSM_AVCTP_BROWSE);
38    pub const DYNAMIC: Self = Self(fidl_bredr::PSM_DYNAMIC);
39
40    pub fn new(value: u16) -> Self {
41        Self(value)
42    }
43}
44
45impl From<Psm> for u16 {
46    fn from(src: Psm) -> u16 {
47        src.0
48    }
49}
50
51/// Try to interpret a DataElement as a ProfileDesciptor.
52/// Returns None if the DataElement is not in the correct format to represent a ProfileDescriptor.
53pub fn elem_to_profile_descriptor(elem: &fidl_bredr::DataElement) -> Option<ProfileDescriptor> {
54    if let fidl_bredr::DataElement::Sequence(seq) = elem {
55        if seq.len() != 2 {
56            return None;
57        }
58
59        if seq[0].is_none() {
60            return None;
61        }
62        let profile_id = match **seq[0].as_ref().expect("not none") {
63            fidl_bredr::DataElement::Uuid(uuid) => {
64                let uuid: Uuid = uuid.into();
65                match uuid.try_into() {
66                    Err(_) => return None,
67                    Ok(profile_id) => profile_id,
68                }
69            }
70            _ => return None,
71        };
72
73        if seq[1].is_none() {
74            return None;
75        }
76        let [major_version, minor_version] = match **seq[1].as_ref().expect("not none") {
77            fidl_bredr::DataElement::Uint16(val) => val.to_be_bytes(),
78            _ => return None,
79        };
80        return Some(ProfileDescriptor {
81            profile_id: Some(profile_id),
82            major_version: Some(major_version),
83            minor_version: Some(minor_version),
84            ..Default::default()
85        });
86    }
87    None
88}
89
90/// Find an element representing the Bluetooth Profile Descriptor List in `attributes`, and
91/// convert the elements in the list into ProfileDescriptors.
92/// Returns an Error if no matching element was found, or if any element of the list couldn't be converted
93/// into a ProfileDescriptor.
94pub fn find_profile_descriptors(
95    attributes: &[fidl_bredr::Attribute],
96) -> Result<Vec<ProfileDescriptor>, Error> {
97    let attr = attributes
98        .iter()
99        .find(|a| a.id == Some(ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST))
100        .ok_or_else(|| Error::profile("missing profile descriptor"))?;
101
102    let Some(fidl_bredr::DataElement::Sequence(profiles)) = &attr.element else {
103        return Err(Error::profile("attribute element is invalidly formatted"));
104    };
105    let mut result = Vec::new();
106    for elem in profiles {
107        let elem = elem.as_ref().ok_or_else(|| Error::profile("null DataElement in sequence"))?;
108        result.push(
109            elem_to_profile_descriptor(&*elem)
110                .ok_or_else(|| Error::profile("couldn't convert to a ProfileDescriptor"))?,
111        );
112    }
113    if result.is_empty() { Err(Error::profile("no profile descriptor found")) } else { Ok(result) }
114}
115
116pub fn profile_descriptor_to_assigned(profile_desc: &ProfileDescriptor) -> Option<AssignedNumber> {
117    let Some(id) = profile_desc.profile_id else {
118        return None;
119    };
120    SERVICE_CLASS_UUIDS.iter().find(|scn| id.into_primitive() == scn.number).cloned()
121}
122
123/// Returns the PSM from the provided `protocol`. Returns None if the protocol
124/// is not L2CAP or does not contain a PSM.
125pub fn psm_from_protocol(protocol: &Vec<ProtocolDescriptor>) -> Option<Psm> {
126    for descriptor in protocol {
127        if descriptor.protocol == fidl_bredr::ProtocolIdentifier::L2Cap {
128            if descriptor.params.len() != 1 {
129                return None;
130            }
131
132            if let DataElement::Uint16(psm) = descriptor.params[0] {
133                return Some(Psm::new(psm));
134            }
135            return None;
136        }
137    }
138    None
139}
140
141/// Returns the RFCOMM or L2CAP channel number from the provided `protocol`.
142/// Returns an Error if the protocol is not a valid L2CAP or RFCOMM service.
143pub fn channel_number_from_protocol(
144    protocol: &Vec<fidl_bredr::ProtocolDescriptor>,
145) -> Result<i32, Error> {
146    for prot in protocol {
147        let Some(params) = prot.params.as_ref() else {
148            return Err(Error::profile("Invalid service definition"));
149        };
150        if prot.protocol == Some(fidl_bredr::ProtocolIdentifier::L2Cap) {
151            // Get the L2CAP PSM from the protocol descriptor. May be empty if the service
152            // requests RFCOMM. If so, we can skip to the next entry in the protocol list.
153            let [fidl_bredr::DataElement::Uint16(l2cap_port)] = params[..] else {
154                continue;
155            };
156            return Ok(l2cap_port.into());
157        }
158
159        if prot.protocol == Some(fidl_bredr::ProtocolIdentifier::Rfcomm) {
160            // If RFCOMM is specified, then the RFCOMM port must be populated.
161            let [fidl_bredr::DataElement::Uint8(rfcomm_port)] = params[..] else {
162                return Err(Error::profile("Invalid RFCOMM service definition"));
163            };
164            return Ok(rfcomm_port.into());
165        }
166    }
167    Err(Error::profile("ProtocolDescriptor missing channel number"))
168}
169
170/// Returns the L2CAP or RFCOMM channel number from the BR/EDR connection parameters.
171/// Returns Error if the provided `parameters` are invalid.
172pub fn channel_number_from_parameters(
173    parameters: &fidl_bredr::ConnectParameters,
174) -> Result<i32, Error> {
175    match parameters {
176        fidl_bredr::ConnectParameters::Rfcomm(fidl_bredr::RfcommParameters {
177            channel: Some(port),
178            ..
179        }) => Ok((*port).into()),
180        fidl_bredr::ConnectParameters::L2cap(fidl_bredr::L2capParameters {
181            psm: Some(psm),
182            ..
183        }) => Ok((*psm).into()),
184        _ => Err(Error::profile(format!("Invalid parameters: {parameters:?}"))),
185    }
186}
187
188/// Search for Service Class UUIDs of well known services from a list of attributes (such as returned via Service Search)
189pub fn find_service_classes(
190    attributes: &[fidl_fuchsia_bluetooth_bredr::Attribute],
191) -> Vec<AssignedNumber> {
192    let uuids = find_all_service_classes(attributes);
193    SERVICE_CLASS_UUIDS
194        .iter()
195        .filter(|scn| uuids.contains(&Uuid::new16(scn.number)))
196        .cloned()
197        .collect()
198}
199
200/// Search for all Service Class UUID from a list of attributes (such as returned via Service Search)
201pub fn find_all_service_classes(
202    attributes: &[fidl_fuchsia_bluetooth_bredr::Attribute],
203) -> Vec<Uuid> {
204    let Some(attr) = attributes.iter().find(|a| a.id == Some(ATTR_SERVICE_CLASS_ID_LIST)) else {
205        return vec![];
206    };
207    let Some(fidl_fuchsia_bluetooth_bredr::DataElement::Sequence(elems)) = &attr.element else {
208        return vec![];
209    };
210    elems
211        .iter()
212        .filter_map(|e| {
213            e.as_ref().and_then(|e| match **e {
214                fidl_fuchsia_bluetooth_bredr::DataElement::Uuid(uuid) => Some(uuid.into()),
215                _ => None,
216            })
217        })
218        .collect()
219}
220
221/// Given two SecurityRequirements, combines both into requirements as strict as either.
222/// A stricter SecurityRequirements is defined as:
223///   1) Authentication required is stricter than not.
224///   2) Secure Connections required is stricter than not.
225pub fn combine_security_requirements(
226    reqs: &SecurityRequirements,
227    other: &SecurityRequirements,
228) -> SecurityRequirements {
229    let authentication_required =
230        match (reqs.authentication_required, other.authentication_required) {
231            (Some(true), _) | (_, Some(true)) => Some(true),
232            (Some(x), None) | (None, Some(x)) => Some(x),
233            _ => None,
234        };
235    let secure_connections_required =
236        match (reqs.secure_connections_required, other.secure_connections_required) {
237            (Some(true), _) | (_, Some(true)) => Some(true),
238            (Some(x), None) | (None, Some(x)) => Some(x),
239            _ => None,
240        };
241    SecurityRequirements { authentication_required, secure_connections_required }
242}
243
244/// Given two ChannelParameters, combines both into a set of ChannelParameters
245/// with the least requesting of resources.
246/// This is defined as:
247///   1) Basic requires fewer resources than ERTM.
248///   2) A smaller SDU size is more restrictive.
249pub fn combine_channel_parameters(
250    params: &ChannelParameters,
251    other: &ChannelParameters,
252) -> ChannelParameters {
253    let channel_mode = match (params.channel_mode, other.channel_mode) {
254        (Some(fidl_bt::ChannelMode::Basic), _) | (_, Some(fidl_bt::ChannelMode::Basic)) => {
255            Some(fidl_bt::ChannelMode::Basic)
256        }
257        (Some(x), None) | (None, Some(x)) => Some(x),
258        _ => None,
259    };
260    let max_rx_sdu_size = match (params.max_rx_sdu_size, other.max_rx_sdu_size) {
261        (Some(rx1), Some(rx2)) => Some(min(rx1, rx2)),
262        (Some(x), None) | (None, Some(x)) => Some(x),
263        _ => None,
264    };
265    let security_requirements = match (&params.security_requirements, &other.security_requirements)
266    {
267        (Some(reqs1), Some(reqs2)) => Some(combine_security_requirements(reqs1, reqs2)),
268        (Some(reqs), _) | (_, Some(reqs)) => Some(reqs.clone()),
269        _ => None,
270    };
271    ChannelParameters { channel_mode, max_rx_sdu_size, security_requirements }
272}
273
274/// The basic building block for elements in a SDP record.
275/// Corresponds directly to the FIDL `DataElement` definition - with the extra
276/// properties of Clone and PartialEq.
277/// See [fuchsia.bluetooth.bredr.DataElement] for more documentation.
278#[derive(Clone, Debug, PartialEq)]
279pub enum DataElement {
280    Int8(i8),
281    Int16(i16),
282    Int32(i32),
283    Int64(i64),
284    Uint8(u8),
285    Uint16(u16),
286    Uint32(u32),
287    Uint64(u64),
288    Str(Vec<u8>),
289    Url(String),
290    Uuid(fidl_bt::Uuid),
291    Bool(bool),
292    Sequence(Vec<Box<DataElement>>),
293    Alternatives(Vec<Box<DataElement>>),
294}
295
296impl TryFrom<&fidl_bredr::DataElement> for DataElement {
297    type Error = Error;
298
299    fn try_from(src: &fidl_bredr::DataElement) -> Result<DataElement, Error> {
300        use fidl_bredr::DataElement as fDataElement;
301        let element = match src {
302            fDataElement::Int8(x) => DataElement::Int8(*x),
303            fDataElement::Int16(x) => DataElement::Int16(*x),
304            fDataElement::Int32(x) => DataElement::Int32(*x),
305            fDataElement::Int64(x) => DataElement::Int64(*x),
306            fDataElement::Uint8(x) => DataElement::Uint8(*x),
307            fDataElement::Uint16(x) => DataElement::Uint16(*x),
308            fDataElement::Uint32(x) => DataElement::Uint32(*x),
309            fDataElement::Uint64(x) => DataElement::Uint64(*x),
310            // TODO(https://fxbug.dev/42058871) Replace clones with moves where possible.
311            fDataElement::Str(v) => DataElement::Str(v.clone()),
312            fDataElement::Url(s) => DataElement::Url(s.to_string()),
313            fDataElement::Uuid(uuid) => DataElement::Uuid(uuid.clone()),
314            fDataElement::B(b) => DataElement::Bool(*b),
315            fDataElement::Sequence(x) => {
316                let mapped = x
317                    .into_iter()
318                    .filter_map(|opt| {
319                        opt.as_ref().map(|t| match DataElement::try_from(&**t) {
320                            Ok(elem) => Ok(Box::new(elem)),
321                            Err(err) => Err(err),
322                        })
323                    })
324                    .collect::<Result<Vec<_>, Error>>()?;
325                DataElement::Sequence(mapped)
326            }
327            fDataElement::Alternatives(x) => {
328                let mapped = x
329                    .into_iter()
330                    .filter_map(|opt| {
331                        opt.as_ref().map(|t| match DataElement::try_from(&**t) {
332                            Ok(elem) => Ok(Box::new(elem)),
333                            Err(err) => Err(err),
334                        })
335                    })
336                    .collect::<Result<Vec<_>, Error>>()?;
337                DataElement::Alternatives(mapped)
338            }
339            _ => return Err(Error::conversion("Unknown DataElement type")),
340        };
341        Ok(element)
342    }
343}
344
345impl From<&DataElement> for fidl_bredr::DataElement {
346    fn from(src: &DataElement) -> fidl_bredr::DataElement {
347        use fidl_bredr::DataElement as fDataElement;
348        match src {
349            DataElement::Int8(x) => fDataElement::Int8(*x),
350            DataElement::Int16(x) => fDataElement::Int16(*x),
351            DataElement::Int32(x) => fDataElement::Int32(*x),
352            DataElement::Int64(x) => fDataElement::Int64(*x),
353            DataElement::Uint8(x) => fDataElement::Uint8(*x),
354            DataElement::Uint16(x) => fDataElement::Uint16(*x),
355            DataElement::Uint32(x) => fDataElement::Uint32(*x),
356            DataElement::Uint64(x) => fDataElement::Uint64(*x),
357            DataElement::Str(v) => fDataElement::Str(v.clone()),
358            DataElement::Url(s) => fDataElement::Url(s.to_string()),
359            DataElement::Uuid(uuid) => fDataElement::Uuid(uuid.clone()),
360            DataElement::Bool(b) => fDataElement::B(*b),
361            DataElement::Sequence(x) => {
362                let mapped = x
363                    .into_iter()
364                    .map(|t| Some(Box::new(fDataElement::from(&**t))))
365                    .collect::<Vec<_>>();
366                fDataElement::Sequence(mapped)
367            }
368            DataElement::Alternatives(x) => {
369                let mapped = x
370                    .into_iter()
371                    .map(|t| Some(Box::new(fDataElement::from(&**t))))
372                    .collect::<Vec<_>>();
373                fDataElement::Alternatives(mapped)
374            }
375        }
376    }
377}
378
379#[derive(Debug, PartialEq)]
380pub struct DataElementConversionError {
381    pub data_element: DataElement,
382}
383
384// Macro for generating impls for converting between rust types and their DataElement wrappers.
385macro_rules! generate_data_element_conversion {
386    ($variant: ident, $type: ty) => {
387        impl TryFrom<DataElement> for $type {
388            type Error = DataElementConversionError;
389
390            fn try_from(data_element: DataElement) -> Result<$type, DataElementConversionError> {
391                match data_element {
392                    DataElement::$variant(x) => Ok(x),
393                    _ => Err(DataElementConversionError { data_element }),
394                }
395            }
396        }
397
398        impl From<$type> for DataElement {
399            fn from(x: $type) -> DataElement {
400                DataElement::$variant(x)
401            }
402        }
403    };
404}
405
406// Generate the impls for converting between rust types and their DataElement wrappers.
407generate_data_element_conversion!(Int8, i8);
408generate_data_element_conversion!(Int16, i16);
409generate_data_element_conversion!(Int32, i32);
410generate_data_element_conversion!(Int64, i64);
411generate_data_element_conversion!(Uint8, u8);
412generate_data_element_conversion!(Uint16, u16);
413generate_data_element_conversion!(Uint32, u32);
414generate_data_element_conversion!(Uint64, u64);
415generate_data_element_conversion!(Str, Vec<u8>);
416generate_data_element_conversion!(Uuid, fidl_bt::Uuid);
417generate_data_element_conversion!(Url, String);
418generate_data_element_conversion!(Bool, bool);
419
420/// Information about a communications protocol.
421/// Corresponds directly to the FIDL `ProtocolDescriptor` definition - with the extra
422/// properties of Clone and PartialEq.
423/// See [fuchsia.bluetooth.bredr.ProtocolDescriptor] for more documentation.
424#[derive(Clone, Debug, PartialEq)]
425pub struct ProtocolDescriptor {
426    pub protocol: fidl_bredr::ProtocolIdentifier,
427    pub params: Vec<DataElement>,
428}
429
430impl TryFrom<&fidl_bredr::ProtocolDescriptor> for ProtocolDescriptor {
431    type Error = Error;
432
433    fn try_from(src: &fidl_bredr::ProtocolDescriptor) -> Result<ProtocolDescriptor, Self::Error> {
434        let Some(protocol) = src.protocol else {
435            return Err(Error::missing("Missing ProtocolDescriptor.protocol"));
436        };
437        let params = src.params.as_ref().map_or(Ok(vec![]), |elems| {
438            elems
439                .into_iter()
440                .map(|elem| DataElement::try_from(elem))
441                .collect::<Result<Vec<DataElement>, Error>>()
442        })?;
443        Ok(ProtocolDescriptor { protocol, params })
444    }
445}
446
447impl From<&ProtocolDescriptor> for fidl_bredr::ProtocolDescriptor {
448    fn from(src: &ProtocolDescriptor) -> fidl_bredr::ProtocolDescriptor {
449        let params = src.params.iter().map(|elem| fidl_bredr::DataElement::from(elem)).collect();
450        fidl_bredr::ProtocolDescriptor {
451            protocol: Some(src.protocol),
452            params: Some(params),
453            ..Default::default()
454        }
455    }
456}
457
458pub fn l2cap_connect_parameters(
459    psm: Psm,
460    mode: fidl_bt::ChannelMode,
461) -> fidl_bredr::ConnectParameters {
462    fidl_bredr::ConnectParameters::L2cap(fidl_bredr::L2capParameters {
463        psm: Some(psm.into()),
464        parameters: Some(fidl_bt::ChannelParameters {
465            channel_mode: Some(mode),
466            ..fidl_bt::ChannelParameters::default()
467        }),
468        ..fidl_bredr::L2capParameters::default()
469    })
470}
471
472/// A generic attribute used for protocol information.
473/// Corresponds directly to the FIDL `Attribute` definition - with the extra
474/// properties of Clone and PartialEq.
475/// See [fuchsia.bluetooth.bredr.Attribute] for more documentation.
476#[derive(Clone, Debug, PartialEq)]
477pub struct Attribute {
478    pub id: u16,
479    pub element: DataElement,
480}
481
482impl TryFrom<&fidl_bredr::Attribute> for Attribute {
483    type Error = Error;
484
485    fn try_from(src: &fidl_bredr::Attribute) -> Result<Attribute, Self::Error> {
486        let Some(id) = src.id else {
487            return Err(Error::missing("Attribute.id"));
488        };
489        let Some(element) = src.element.as_ref() else {
490            return Err(Error::missing("Attribute.element"));
491        };
492        let element = DataElement::try_from(element)?;
493        Ok(Attribute { id, element })
494    }
495}
496
497impl From<&Attribute> for fidl_bredr::Attribute {
498    fn from(src: &Attribute) -> fidl_bredr::Attribute {
499        fidl_bredr::Attribute {
500            id: Some(src.id),
501            element: Some(fidl_bredr::DataElement::from(&src.element)),
502            ..Default::default()
503        }
504    }
505}
506
507/// Human-readable information about a service.
508/// Corresponds directly to the FIDL `Information` definition - with the extra
509/// properties of Clone and PartialEq.
510/// See [fuchsia.bluetooth.bredr.Information] for more documentation.
511#[derive(Clone, Debug, PartialEq)]
512pub struct Information {
513    pub language: String,
514    pub name: Option<String>,
515    pub description: Option<String>,
516    pub provider: Option<String>,
517}
518
519impl TryFrom<&fidl_bredr::Information> for Information {
520    type Error = Error;
521
522    fn try_from(src: &fidl_bredr::Information) -> Result<Information, Self::Error> {
523        let language = match src.language.as_ref().map(String::as_str) {
524            None | Some("") => return Err(Error::missing("bredr.Information.language")),
525            Some(l) => l.to_string(),
526        };
527
528        Ok(Information {
529            language,
530            name: src.name.clone(),
531            description: src.description.clone(),
532            provider: src.provider.clone(),
533        })
534    }
535}
536
537impl TryFrom<&Information> for fidl_bredr::Information {
538    type Error = Error;
539
540    fn try_from(src: &Information) -> Result<fidl_bredr::Information, Self::Error> {
541        if src.language.is_empty() {
542            return Err(Error::missing("Information.language"));
543        }
544
545        Ok(fidl_bredr::Information {
546            language: Some(src.language.clone()),
547            name: src.name.clone(),
548            description: src.description.clone(),
549            provider: src.provider.clone(),
550            ..Default::default()
551        })
552    }
553}
554
555/// Definition of a service that is to be advertised via Bluetooth BR/EDR.
556/// Corresponds directly to the FIDL `ServiceDefinition` definition - with the extra
557/// properties of Clone and PartialEq.
558/// See [fuchsia.bluetooth.bredr.ServiceDefinition] for more documentation.
559#[derive(Clone, Debug, Default, PartialEq)]
560pub struct ServiceDefinition {
561    pub service_class_uuids: Vec<Uuid>,
562    pub protocol_descriptor_list: Vec<ProtocolDescriptor>,
563    pub additional_protocol_descriptor_lists: Vec<Vec<ProtocolDescriptor>>,
564    pub profile_descriptors: Vec<fidl_bredr::ProfileDescriptor>,
565    pub information: Vec<Information>,
566    pub additional_attributes: Vec<Attribute>,
567}
568
569impl ServiceDefinition {
570    pub fn try_into_fidl(this: &Vec<Self>) -> Result<Vec<fidl_bredr::ServiceDefinition>, Error> {
571        this.iter().map(fidl_bredr::ServiceDefinition::try_from).collect::<Result<Vec<_>, _>>()
572    }
573
574    pub fn try_from_fidl(src: &Vec<fidl_bredr::ServiceDefinition>) -> Result<Vec<Self>, Error> {
575        src.iter().map(Self::try_from).collect::<Result<Vec<_>, _>>()
576    }
577
578    /// Returns the primary PSM associated with this ServiceDefinition.
579    pub fn primary_psm(&self) -> Option<Psm> {
580        psm_from_protocol(&self.protocol_descriptor_list)
581    }
582
583    /// Returns the additional PSMs associated with this ServiceDefinition.
584    pub fn additional_psms(&self) -> HashSet<Psm> {
585        self.additional_protocol_descriptor_lists
586            .iter()
587            .filter_map(|protocol| psm_from_protocol(protocol))
588            .collect()
589    }
590
591    /// Returns the PSM in the GOEP L2CAP Attribute, if provided.
592    pub fn goep_l2cap_psm(&self) -> Option<Psm> {
593        const GOEP_L2CAP_PSM_ATTRIBUTE: u16 = 0x0200;
594        let Some(attribute) =
595            self.additional_attributes.iter().find(|attr| attr.id == GOEP_L2CAP_PSM_ATTRIBUTE)
596        else {
597            return None;
598        };
599
600        let DataElement::Uint16(psm) = attribute.element else {
601            return None;
602        };
603
604        Some(Psm::new(psm))
605    }
606
607    /// Returns all the PSMs associated with this ServiceDefinition.
608    ///
609    /// It's possible that the definition doesn't provide any PSMs, in which
610    /// case the returned set will be empty.
611    pub fn psm_set(&self) -> HashSet<Psm> {
612        let mut psms = self.additional_psms();
613        if let Some(psm) = self.primary_psm() {
614            let _ = psms.insert(psm);
615        }
616        if let Some(psm) = self.goep_l2cap_psm() {
617            let _ = psms.insert(psm);
618        }
619
620        psms
621    }
622}
623
624impl TryFrom<&fidl_bredr::ServiceDefinition> for ServiceDefinition {
625    type Error = Error;
626
627    fn try_from(src: &fidl_bredr::ServiceDefinition) -> Result<ServiceDefinition, Self::Error> {
628        let service_class_uuids = match &src.service_class_uuids {
629            Some(uuids) if !uuids.is_empty() => uuids.iter().map(Uuid::from).collect(),
630            _ => {
631                return Err(Error::conversion(
632                    "bredr.ServiceDefinition.service_class_uuids is empty",
633                ));
634            }
635        };
636
637        let protocol_descriptor_list: Vec<ProtocolDescriptor> =
638            src.protocol_descriptor_list.as_ref().map_or(Ok(vec![]), |p| {
639                p.into_iter()
640                    .map(|d| ProtocolDescriptor::try_from(d))
641                    .collect::<Result<Vec<ProtocolDescriptor>, Error>>()
642            })?;
643        let additional_protocol_descriptor_lists: Vec<Vec<ProtocolDescriptor>> =
644            src.additional_protocol_descriptor_lists.as_ref().map_or(Ok(vec![]), |desc_lists| {
645                desc_lists
646                    .into_iter()
647                    .map(|desc_list| {
648                        desc_list
649                            .into_iter()
650                            .map(|d| ProtocolDescriptor::try_from(d))
651                            .collect::<Result<Vec<ProtocolDescriptor>, Error>>()
652                    })
653                    .collect::<Result<Vec<Vec<ProtocolDescriptor>>, Error>>()
654            })?;
655        let profile_descriptors: Vec<fidl_bredr::ProfileDescriptor> =
656            src.profile_descriptors.clone().unwrap_or(vec![]);
657        let information: Result<Vec<Information>, Error> =
658            src.information.as_ref().map_or(Ok(vec![]), |infos| {
659                infos.into_iter().map(|i| Information::try_from(i)).collect()
660            });
661        let additional_attributes: Vec<Attribute> =
662            src.additional_attributes.as_ref().map_or(Ok(vec![]), |attrs| {
663                attrs
664                    .into_iter()
665                    .map(|a| Attribute::try_from(a))
666                    .collect::<Result<Vec<Attribute>, Error>>()
667            })?;
668
669        Ok(ServiceDefinition {
670            service_class_uuids,
671            protocol_descriptor_list,
672            additional_protocol_descriptor_lists,
673            profile_descriptors,
674            information: information?,
675            additional_attributes,
676        })
677    }
678}
679
680impl TryFrom<&ServiceDefinition> for fidl_bredr::ServiceDefinition {
681    type Error = Error;
682
683    fn try_from(src: &ServiceDefinition) -> Result<fidl_bredr::ServiceDefinition, Self::Error> {
684        if src.service_class_uuids.is_empty() {
685            return Err(Error::conversion("ServiceDefinitions.service_class_uuids is empty"));
686        }
687        let service_class_uuids = src.service_class_uuids.iter().map(fidl_bt::Uuid::from).collect();
688
689        let protocol_descriptor_list: Vec<fidl_bredr::ProtocolDescriptor> = src
690            .protocol_descriptor_list
691            .iter()
692            .map(|d| fidl_bredr::ProtocolDescriptor::from(d))
693            .collect();
694        let additional_protocol_descriptor_lists: Vec<Vec<fidl_bredr::ProtocolDescriptor>> = src
695            .additional_protocol_descriptor_lists
696            .iter()
697            .map(|desc_list| {
698                desc_list.into_iter().map(|d| fidl_bredr::ProtocolDescriptor::from(d)).collect()
699            })
700            .collect();
701        let profile_descriptors: Vec<fidl_bredr::ProfileDescriptor> =
702            src.profile_descriptors.clone();
703        let information: Result<Vec<fidl_bredr::Information>, Error> =
704            src.information.iter().map(|i| fidl_bredr::Information::try_from(i)).collect();
705        let additional_attributes: Vec<fidl_bredr::Attribute> =
706            src.additional_attributes.iter().map(|a| fidl_bredr::Attribute::from(a)).collect();
707
708        Ok(fidl_bredr::ServiceDefinition {
709            service_class_uuids: Some(service_class_uuids),
710            protocol_descriptor_list: Some(protocol_descriptor_list),
711            additional_protocol_descriptor_lists: Some(additional_protocol_descriptor_lists),
712            profile_descriptors: Some(profile_descriptors),
713            information: Some(information?),
714            additional_attributes: Some(additional_attributes),
715            ..Default::default()
716        })
717    }
718}
719
720/// Authentication and permission requirements for an advertised service.
721/// Corresponds directly to the FIDL `SecurityRequirements` definition - with the extra properties
722/// of Clone and PartialEq.
723/// See [fuchsia.bluetooth.bredr.SecurityRequirements] for more documentation.
724#[derive(Clone, Debug, Default, PartialEq)]
725pub struct SecurityRequirements {
726    pub authentication_required: Option<bool>,
727    pub secure_connections_required: Option<bool>,
728}
729
730impl From<&fidl_bt::SecurityRequirements> for SecurityRequirements {
731    fn from(src: &fidl_bt::SecurityRequirements) -> SecurityRequirements {
732        SecurityRequirements {
733            authentication_required: src.authentication_required,
734            secure_connections_required: src.secure_connections_required,
735        }
736    }
737}
738
739impl From<&SecurityRequirements> for fidl_bt::SecurityRequirements {
740    fn from(src: &SecurityRequirements) -> fidl_bt::SecurityRequirements {
741        fidl_bt::SecurityRequirements {
742            authentication_required: src.authentication_required,
743            secure_connections_required: src.secure_connections_required,
744            ..Default::default()
745        }
746    }
747}
748
749/// Minimum SDU size the service is capable of accepting.
750/// See [fuchsia.bluetooth.bredr.ChannelParameters] for more documentation.
751const MIN_RX_SDU_SIZE: u16 = 48;
752
753/// Preferred L2CAP channel parameters for an advertised service.
754/// Corresponds directly to the FIDL `ChannelParameters` definition - with the extra properties
755/// of Clone and PartialEq.
756/// The invariants of the FIDL definition are enforced - the max SDU size must be >= 48.
757/// See [fuchsia.bluetooth.bredr.ChannelParameters] for more documentation.
758#[derive(Clone, Debug, Default, PartialEq)]
759pub struct ChannelParameters {
760    pub channel_mode: Option<fidl_bt::ChannelMode>,
761    pub max_rx_sdu_size: Option<u16>,
762    pub security_requirements: Option<SecurityRequirements>,
763}
764
765impl TryFrom<&fidl_bt::ChannelParameters> for ChannelParameters {
766    type Error = Error;
767
768    fn try_from(src: &fidl_bt::ChannelParameters) -> Result<ChannelParameters, Self::Error> {
769        if let Some(size) = src.max_rx_packet_size {
770            if size < MIN_RX_SDU_SIZE {
771                return Err(Error::conversion(format!(
772                    "bredr.ChannelParameters.max_rx_sdu_size is too small: {size}"
773                )));
774            }
775        }
776
777        Ok(ChannelParameters {
778            channel_mode: src.channel_mode,
779            max_rx_sdu_size: src.max_rx_packet_size,
780            security_requirements: src
781                .security_requirements
782                .as_ref()
783                .map(SecurityRequirements::from),
784        })
785    }
786}
787
788impl TryFrom<&ChannelParameters> for fidl_bt::ChannelParameters {
789    type Error = Error;
790
791    fn try_from(src: &ChannelParameters) -> Result<fidl_bt::ChannelParameters, Self::Error> {
792        if let Some(size) = src.max_rx_sdu_size {
793            if size < MIN_RX_SDU_SIZE {
794                return Err(Error::conversion(format!(
795                    "ChannelParameters.max_rx_sdu_size is too small: {size}"
796                )));
797            }
798        }
799
800        Ok(fidl_bt::ChannelParameters {
801            channel_mode: src.channel_mode,
802            max_rx_packet_size: src.max_rx_sdu_size,
803            security_requirements: src
804                .security_requirements
805                .as_ref()
806                .map(fidl_bt::SecurityRequirements::from),
807            ..Default::default()
808        })
809    }
810}
811
812#[derive(Debug, Clone, ValidFidlTable, PartialEq)]
813#[fidl_table_src(fidl_bredr::ScoConnectionParameters)]
814pub struct ValidScoConnectionParameters {
815    pub parameter_set: fidl_bredr::HfpParameterSet,
816    pub air_coding_format: fidl_bt::AssignedCodingFormat,
817    pub air_frame_size: u16,
818    pub io_bandwidth: u32,
819    pub io_coding_format: fidl_bt::AssignedCodingFormat,
820    pub io_frame_size: u16,
821    #[fidl_field_type(optional)]
822    pub io_pcm_data_format: Option<fidl_fuchsia_hardware_audio::SampleFormat>,
823    #[fidl_field_type(optional)]
824    pub io_pcm_sample_payload_msb_position: Option<u8>,
825    pub path: fidl_bredr::DataPath,
826}
827
828#[cfg(target_os = "fuchsia")]
829impl Unit for ValidScoConnectionParameters {
830    type Data = inspect::Node;
831    fn inspect_create(&self, parent: &inspect::Node, name: impl AsRef<str>) -> Self::Data {
832        let mut node = parent.create_child(name.as_ref());
833        self.inspect_update(&mut node);
834        node
835    }
836
837    fn inspect_update(&self, data: &mut Self::Data) {
838        data.record_string("parameter_set", &format!("{:?}", self.parameter_set));
839        data.record_string("air_coding_format", &format!("{:?}", self.air_coding_format));
840        data.record_uint("air_frame_size", self.air_frame_size.into());
841        data.record_uint("io_bandwidth", self.io_bandwidth.into());
842        data.record_string("io_coding_format", &format!("{:?}", self.io_coding_format));
843        data.record_uint("io_frame_size", self.io_frame_size.into());
844        if let Some(io_pcm_data_format) = &self.io_pcm_data_format {
845            data.record_string("io_pcm_data_format", &format!("{:?}", io_pcm_data_format));
846        }
847        if let Some(io_pcm_sample_payload_msb_position) = &self.io_pcm_sample_payload_msb_position {
848            data.record_uint(
849                "io_pcm_sample_payload_msb_position",
850                (*io_pcm_sample_payload_msb_position).into(),
851            );
852        }
853        data.record_string("path", &format!("{:?}", self.path));
854    }
855}
856
857#[cfg(target_os = "fuchsia")]
858impl Inspect for &mut ValidScoConnectionParameters {
859    fn iattach(self, parent: &inspect::Node, name: impl AsRef<str>) -> Result<(), AttachError> {
860        // The created node is owned by the provided `parent`.
861        parent.record(self.inspect_create(parent, name));
862        Ok(())
863    }
864}
865
866#[cfg(test)]
867mod tests {
868    use super::*;
869    use diagnostics_assertions::assert_data_tree;
870
871    #[test]
872    fn test_find_descriptors_fails_with_no_descriptors() {
873        assert!(find_profile_descriptors(&[]).is_err());
874
875        let mut attributes = vec![fidl_bredr::Attribute {
876            id: Some(0x3001),
877            element: Some(fidl_bredr::DataElement::Uint32(0xF00FC0DE)),
878            ..Default::default()
879        }];
880
881        assert!(find_profile_descriptors(&attributes).is_err());
882
883        // Wrong element type
884        attributes.push(fidl_bredr::Attribute {
885            id: Some(fidl_bredr::ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST),
886            element: Some(fidl_bredr::DataElement::Uint32(0xABADC0DE)),
887            ..Default::default()
888        });
889
890        assert!(find_profile_descriptors(&attributes).is_err());
891
892        // Empty sequence
893        attributes[1].element = Some(fidl_bredr::DataElement::Sequence(vec![]));
894
895        assert!(find_profile_descriptors(&attributes).is_err());
896    }
897
898    #[test]
899    fn test_find_descriptors_returns_descriptors() {
900        let attributes = vec![fidl_bredr::Attribute {
901            id: Some(fidl_bredr::ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST),
902            element: Some(fidl_bredr::DataElement::Sequence(vec![
903                Some(Box::new(fidl_bredr::DataElement::Sequence(vec![
904                    Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()))),
905                    Some(Box::new(fidl_bredr::DataElement::Uint16(0x0103))),
906                ]))),
907                Some(Box::new(fidl_bredr::DataElement::Sequence(vec![
908                    Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x113A).into()))),
909                    Some(Box::new(fidl_bredr::DataElement::Uint16(0x0302))),
910                ]))),
911            ])),
912            ..Default::default()
913        }];
914
915        let result = find_profile_descriptors(&attributes);
916        assert!(result.is_ok());
917        let result = result.expect("result");
918        assert_eq!(2, result.len());
919
920        assert_eq!(
921            fidl_bredr::ServiceClassProfileIdentifier::SerialPort,
922            result[0].profile_id.unwrap()
923        );
924        assert_eq!(1, result[0].major_version.unwrap());
925        assert_eq!(3, result[0].minor_version.unwrap());
926    }
927
928    #[test]
929    fn test_find_service_classes_attribute_missing() {
930        assert_eq!(find_service_classes(&[]), Vec::new());
931        let attributes = vec![fidl_bredr::Attribute {
932            id: Some(fidl_bredr::ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST),
933            element: Some(fidl_bredr::DataElement::Sequence(vec![
934                Some(Box::new(fidl_bredr::DataElement::Sequence(vec![
935                    Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()))),
936                    Some(Box::new(fidl_bredr::DataElement::Uint16(0x0103))),
937                ]))),
938                Some(Box::new(fidl_bredr::DataElement::Sequence(vec![
939                    Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x113A).into()))),
940                    Some(Box::new(fidl_bredr::DataElement::Uint16(0x0302))),
941                ]))),
942            ])),
943            ..Default::default()
944        }];
945        assert_eq!(find_service_classes(&attributes), Vec::new());
946    }
947
948    #[test]
949    fn test_find_service_classes_wrong_type() {
950        let attributes = vec![fidl_bredr::Attribute {
951            id: Some(fidl_bredr::ATTR_SERVICE_CLASS_ID_LIST),
952            element: Some(fidl_bredr::DataElement::Uint32(0xc0defae5u32)),
953            ..Default::default()
954        }];
955        assert_eq!(find_service_classes(&attributes), Vec::new());
956    }
957
958    #[test]
959    fn test_find_service_classes_returns_known_classes() {
960        let attribute = fidl_bredr::Attribute {
961            id: Some(fidl_bredr::ATTR_SERVICE_CLASS_ID_LIST),
962            element: Some(fidl_bredr::DataElement::Sequence(vec![Some(Box::new(
963                fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()),
964            ))])),
965            ..Default::default()
966        };
967
968        let result = find_service_classes(&[attribute]);
969        assert_eq!(1, result.len());
970        let assigned_num = result.first().unwrap();
971        assert_eq!(0x1101, assigned_num.number); // 0x1101 is the 16-bit UUID of SerialPort
972        assert_eq!("SerialPort", assigned_num.name);
973
974        let unknown_uuids = fidl_bredr::Attribute {
975            id: Some(fidl_bredr::ATTR_SERVICE_CLASS_ID_LIST),
976            element: Some(fidl_bredr::DataElement::Sequence(vec![
977                Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()))),
978                Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0xc0de).into()))),
979            ])),
980            ..Default::default()
981        };
982
983        // Discards unknown UUIDs
984        let result = find_service_classes(&[unknown_uuids]);
985        assert_eq!(1, result.len());
986        let assigned_num = result.first().unwrap();
987        assert_eq!(0x1101, assigned_num.number); // 0x1101 is the 16-bit UUID of SerialPort
988        assert_eq!("SerialPort", assigned_num.name);
989    }
990
991    #[test]
992    fn test_psm_from_protocol() {
993        let empty = vec![];
994        assert_eq!(None, psm_from_protocol(&empty));
995
996        let no_psm = vec![ProtocolDescriptor {
997            protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
998            params: vec![],
999        }];
1000        assert_eq!(None, psm_from_protocol(&no_psm));
1001
1002        let psm = Psm::new(10);
1003        let valid_psm = vec![ProtocolDescriptor {
1004            protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
1005            params: vec![DataElement::Uint16(psm.into())],
1006        }];
1007        assert_eq!(Some(psm), psm_from_protocol(&valid_psm));
1008
1009        let rfcomm = vec![
1010            ProtocolDescriptor {
1011                protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
1012                params: vec![], // PSM omitted for RFCOMM.
1013            },
1014            ProtocolDescriptor {
1015                protocol: fidl_bredr::ProtocolIdentifier::Rfcomm,
1016                params: vec![DataElement::Uint8(10)], // Server channel
1017            },
1018        ];
1019        assert_eq!(None, psm_from_protocol(&rfcomm));
1020    }
1021
1022    #[test]
1023    fn test_elem_to_profile_descriptor_works() {
1024        let element = fidl_bredr::DataElement::Sequence(vec![
1025            Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()))),
1026            Some(Box::new(fidl_bredr::DataElement::Uint16(0x0103))),
1027        ]);
1028
1029        let descriptor =
1030            elem_to_profile_descriptor(&element).expect("descriptor should be returned");
1031
1032        assert_eq!(
1033            fidl_bredr::ServiceClassProfileIdentifier::SerialPort,
1034            descriptor.profile_id.unwrap()
1035        );
1036        assert_eq!(1, descriptor.major_version.unwrap());
1037        assert_eq!(3, descriptor.minor_version.unwrap());
1038    }
1039
1040    #[test]
1041    fn test_elem_to_profile_descriptor_wrong_element_types() {
1042        let element = fidl_bredr::DataElement::Sequence(vec![
1043            Some(Box::new(fidl_bredr::DataElement::Uint16(0x1101))),
1044            Some(Box::new(fidl_bredr::DataElement::Uint16(0x0103))),
1045        ]);
1046        assert!(elem_to_profile_descriptor(&element).is_none());
1047
1048        let element = fidl_bredr::DataElement::Sequence(vec![
1049            Some(Box::new(fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()))),
1050            Some(Box::new(fidl_bredr::DataElement::Uint32(0x0103))),
1051        ]);
1052        assert!(elem_to_profile_descriptor(&element).is_none());
1053
1054        let element = fidl_bredr::DataElement::Sequence(vec![Some(Box::new(
1055            fidl_bredr::DataElement::Uint32(0x0103),
1056        ))]);
1057        assert!(elem_to_profile_descriptor(&element).is_none());
1058
1059        let element = fidl_bredr::DataElement::Sequence(vec![None]);
1060        assert!(elem_to_profile_descriptor(&element).is_none());
1061
1062        let element = fidl_bredr::DataElement::Uint32(0xDEADC0DE);
1063        assert!(elem_to_profile_descriptor(&element).is_none());
1064    }
1065
1066    #[test]
1067    fn test_invalid_information_fails_gracefully() {
1068        let empty_language = "".to_string();
1069
1070        let invalid_local = Information {
1071            language: empty_language.clone(),
1072            name: None,
1073            description: None,
1074            provider: None,
1075        };
1076        let fidl = fidl_bredr::Information::try_from(&invalid_local);
1077        assert!(fidl.is_err());
1078
1079        // No language.
1080        let local = Information::try_from(&fidl_bredr::Information::default());
1081        assert!(local.is_err());
1082
1083        let empty_lang_fidl =
1084            fidl_bredr::Information { language: Some(empty_language), ..Default::default() };
1085        let local = Information::try_from(&empty_lang_fidl);
1086        assert!(local.is_err());
1087    }
1088
1089    #[test]
1090    fn get_psm_from_empty_service_definition() {
1091        let def = ServiceDefinition {
1092            service_class_uuids: vec![Uuid::new32(1234)],
1093            protocol_descriptor_list: vec![],
1094            additional_protocol_descriptor_lists: vec![],
1095            profile_descriptors: vec![],
1096            information: vec![],
1097            additional_attributes: vec![],
1098        };
1099
1100        assert_eq!(def.primary_psm(), None);
1101        assert_eq!(def.additional_psms(), HashSet::new());
1102        assert_eq!(def.psm_set(), HashSet::new());
1103    }
1104
1105    #[test]
1106    fn test_get_psm_from_service_definition() {
1107        let uuid = Uuid::new32(1234);
1108        let psm1 = Psm(10);
1109        let psm2 = Psm(12);
1110        let psm3 = Psm(4000);
1111        let mut def = ServiceDefinition {
1112            service_class_uuids: vec![uuid],
1113            protocol_descriptor_list: vec![ProtocolDescriptor {
1114                protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
1115                params: vec![DataElement::Uint16(psm1.into())],
1116            }],
1117            additional_protocol_descriptor_lists: vec![],
1118            profile_descriptors: vec![],
1119            information: vec![],
1120            additional_attributes: vec![],
1121        };
1122
1123        // Primary protocol contains one PSM.
1124        assert_eq!(def.primary_psm(), Some(psm1));
1125        assert_eq!(def.additional_psms(), HashSet::new());
1126        assert_eq!(def.psm_set(), HashSet::from([psm1]));
1127
1128        def.additional_protocol_descriptor_lists = vec![
1129            vec![ProtocolDescriptor {
1130                protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
1131                params: vec![DataElement::Uint16(psm2.into())],
1132            }],
1133            vec![ProtocolDescriptor {
1134                protocol: fidl_bredr::ProtocolIdentifier::Avdtp,
1135                params: vec![DataElement::Uint16(0x0103)],
1136            }],
1137        ];
1138
1139        // Additional protocol contains one PSM.
1140        assert_eq!(def.primary_psm(), Some(psm1));
1141        assert_eq!(def.additional_psms(), HashSet::from([psm2]));
1142        assert_eq!(def.psm_set(), HashSet::from([psm1, psm2]));
1143
1144        // Additional attributes contain one PSM.
1145        def.additional_attributes =
1146            vec![Attribute { id: 0x0200, element: DataElement::Uint16(psm3.into()) }];
1147        assert_eq!(def.primary_psm(), Some(psm1));
1148        assert_eq!(def.additional_psms(), HashSet::from([psm2]));
1149        assert_eq!(def.goep_l2cap_psm(), Some(psm3));
1150        assert_eq!(def.psm_set(), HashSet::from([psm1, psm2, psm3]));
1151    }
1152
1153    #[test]
1154    fn test_service_definition_conversions() {
1155        let uuid = fidl_bt::Uuid { value: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] };
1156        let prof_descs = vec![ProfileDescriptor {
1157            profile_id: Some(fidl_bredr::ServiceClassProfileIdentifier::AvRemoteControl),
1158            major_version: Some(1),
1159            minor_version: Some(6),
1160            ..Default::default()
1161        }];
1162        let language = "en".to_string();
1163        let name = "foobar".to_string();
1164        let description = "fake".to_string();
1165        let provider = "random".to_string();
1166        let attribute_id = 0x3001;
1167        let attribute_value = 0xF00FC0DE;
1168
1169        let local = ServiceDefinition {
1170            service_class_uuids: vec![uuid.into()],
1171            protocol_descriptor_list: vec![ProtocolDescriptor {
1172                protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
1173                params: vec![DataElement::Uint16(10)],
1174            }],
1175            additional_protocol_descriptor_lists: vec![
1176                vec![ProtocolDescriptor {
1177                    protocol: fidl_bredr::ProtocolIdentifier::L2Cap,
1178                    params: vec![DataElement::Uint16(12)],
1179                }],
1180                vec![ProtocolDescriptor {
1181                    protocol: fidl_bredr::ProtocolIdentifier::Avdtp,
1182                    params: vec![DataElement::Uint16(3)],
1183                }],
1184            ],
1185            profile_descriptors: prof_descs.clone(),
1186            information: vec![Information {
1187                language: language.clone(),
1188                name: Some(name.clone()),
1189                description: Some(description.clone()),
1190                provider: Some(provider.clone()),
1191            }],
1192            additional_attributes: vec![Attribute {
1193                id: attribute_id,
1194                element: DataElement::Sequence(vec![Box::new(DataElement::Uint32(
1195                    attribute_value,
1196                ))]),
1197            }],
1198        };
1199
1200        let fidl = fidl_bredr::ServiceDefinition {
1201            service_class_uuids: Some(vec![uuid]),
1202            protocol_descriptor_list: Some(vec![fidl_bredr::ProtocolDescriptor {
1203                protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1204                params: Some(vec![fidl_bredr::DataElement::Uint16(10)]),
1205                ..Default::default()
1206            }]),
1207            additional_protocol_descriptor_lists: Some(vec![
1208                vec![fidl_bredr::ProtocolDescriptor {
1209                    protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1210                    params: Some(vec![fidl_bredr::DataElement::Uint16(12)]),
1211                    ..Default::default()
1212                }],
1213                vec![fidl_bredr::ProtocolDescriptor {
1214                    protocol: Some(fidl_bredr::ProtocolIdentifier::Avdtp),
1215                    params: Some(vec![fidl_bredr::DataElement::Uint16(3)]),
1216                    ..Default::default()
1217                }],
1218            ]),
1219            profile_descriptors: Some(prof_descs.clone()),
1220            information: Some(vec![fidl_bredr::Information {
1221                language: Some(language.clone()),
1222                name: Some(name.clone()),
1223                description: Some(description.clone()),
1224                provider: Some(provider.clone()),
1225                ..Default::default()
1226            }]),
1227            additional_attributes: Some(vec![fidl_bredr::Attribute {
1228                id: Some(attribute_id),
1229                element: Some(fidl_bredr::DataElement::Sequence(vec![Some(Box::new(
1230                    fidl_bredr::DataElement::Uint32(attribute_value),
1231                ))])),
1232                ..Default::default()
1233            }]),
1234            ..Default::default()
1235        };
1236
1237        // Converting from local ServiceDefinition to the FIDL ServiceDefinition should work.
1238        let local_to_fidl: fidl_bredr::ServiceDefinition =
1239            fidl_bredr::ServiceDefinition::try_from(&local).expect("should work");
1240        assert_eq!(local_to_fidl, fidl);
1241
1242        // Converting from FIDL ServiceDefinition to the local ServiceDefinition should work.
1243        let fidl_to_local: ServiceDefinition =
1244            ServiceDefinition::try_from(&fidl).expect("should work");
1245        assert_eq!(fidl_to_local, local);
1246    }
1247
1248    #[test]
1249    fn test_invalid_service_definition_fails_gracefully() {
1250        let no_uuids_fidl = fidl_bredr::ServiceDefinition::default();
1251        let fidl_to_local = ServiceDefinition::try_from(&no_uuids_fidl);
1252        assert!(fidl_to_local.is_err());
1253
1254        let empty_uuids_fidl = fidl_bredr::ServiceDefinition {
1255            service_class_uuids: Some(vec![]),
1256            ..Default::default()
1257        };
1258        let fidl_to_local = ServiceDefinition::try_from(&empty_uuids_fidl);
1259        assert!(fidl_to_local.is_err());
1260    }
1261
1262    #[test]
1263    fn test_channel_parameters_conversions() {
1264        let channel_mode = Some(fidl_bt::ChannelMode::EnhancedRetransmission);
1265        let max_rx_sdu_size = Some(MIN_RX_SDU_SIZE);
1266
1267        let local =
1268            ChannelParameters { channel_mode, max_rx_sdu_size, security_requirements: None };
1269        let fidl = fidl_bt::ChannelParameters {
1270            channel_mode,
1271            max_rx_packet_size: max_rx_sdu_size,
1272            ..Default::default()
1273        };
1274
1275        let local_to_fidl =
1276            fidl_bt::ChannelParameters::try_from(&local).expect("conversion should work");
1277        assert_eq!(local_to_fidl, fidl);
1278
1279        let fidl_to_local = ChannelParameters::try_from(&fidl).expect("conversion should work");
1280        assert_eq!(fidl_to_local, local);
1281
1282        // Empty FIDL parameters is OK.
1283        let fidl = fidl_bt::ChannelParameters::default();
1284        let expected = ChannelParameters {
1285            channel_mode: None,
1286            max_rx_sdu_size: None,
1287            security_requirements: None,
1288        };
1289
1290        let fidl_to_local = ChannelParameters::try_from(&fidl).expect("conversion should work");
1291        assert_eq!(fidl_to_local, expected);
1292    }
1293
1294    #[test]
1295    fn test_invalid_channel_parameters_fails_gracefully() {
1296        let too_small_sdu = Some(MIN_RX_SDU_SIZE - 1);
1297        let local = ChannelParameters {
1298            channel_mode: None,
1299            max_rx_sdu_size: too_small_sdu,
1300            security_requirements: None,
1301        };
1302        let fidl =
1303            fidl_bt::ChannelParameters { max_rx_packet_size: too_small_sdu, ..Default::default() };
1304
1305        let local_to_fidl = fidl_bt::ChannelParameters::try_from(&local);
1306        assert!(local_to_fidl.is_err());
1307
1308        let fidl_to_local = ChannelParameters::try_from(&fidl);
1309        assert!(fidl_to_local.is_err());
1310    }
1311
1312    #[test]
1313    fn test_security_requirements_conversions() {
1314        let authentication_required = Some(false);
1315        let secure_connections_required = Some(true);
1316
1317        let local = SecurityRequirements { authentication_required, secure_connections_required };
1318        let fidl = fidl_bt::SecurityRequirements {
1319            authentication_required,
1320            secure_connections_required,
1321            ..Default::default()
1322        };
1323
1324        let local_to_fidl = fidl_bt::SecurityRequirements::from(&local);
1325        assert_eq!(local_to_fidl, fidl);
1326
1327        let fidl_to_local = SecurityRequirements::from(&fidl);
1328        assert_eq!(fidl_to_local, local);
1329    }
1330
1331    #[test]
1332    fn test_combine_security_requirements() {
1333        let req1 = SecurityRequirements {
1334            authentication_required: None,
1335            secure_connections_required: None,
1336        };
1337        let req2 = SecurityRequirements {
1338            authentication_required: None,
1339            secure_connections_required: None,
1340        };
1341        let expected = SecurityRequirements {
1342            authentication_required: None,
1343            secure_connections_required: None,
1344        };
1345        assert_eq!(combine_security_requirements(&req1, &req2), expected);
1346
1347        let req1 = SecurityRequirements {
1348            authentication_required: Some(true),
1349            secure_connections_required: None,
1350        };
1351        let req2 = SecurityRequirements {
1352            authentication_required: None,
1353            secure_connections_required: Some(true),
1354        };
1355        let expected = SecurityRequirements {
1356            authentication_required: Some(true),
1357            secure_connections_required: Some(true),
1358        };
1359        assert_eq!(combine_security_requirements(&req1, &req2), expected);
1360
1361        let req1 = SecurityRequirements {
1362            authentication_required: Some(false),
1363            secure_connections_required: Some(true),
1364        };
1365        let req2 = SecurityRequirements {
1366            authentication_required: None,
1367            secure_connections_required: Some(true),
1368        };
1369        let expected = SecurityRequirements {
1370            authentication_required: Some(false),
1371            secure_connections_required: Some(true),
1372        };
1373        assert_eq!(combine_security_requirements(&req1, &req2), expected);
1374
1375        let req1 = SecurityRequirements {
1376            authentication_required: Some(true),
1377            secure_connections_required: Some(false),
1378        };
1379        let req2 = SecurityRequirements {
1380            authentication_required: Some(false),
1381            secure_connections_required: Some(true),
1382        };
1383        let expected = SecurityRequirements {
1384            authentication_required: Some(true),
1385            secure_connections_required: Some(true),
1386        };
1387        assert_eq!(combine_security_requirements(&req1, &req2), expected);
1388    }
1389
1390    #[test]
1391    fn test_combine_channel_parameters() {
1392        let p1 = ChannelParameters::default();
1393        let p2 = ChannelParameters::default();
1394        let expected = ChannelParameters::default();
1395        assert_eq!(combine_channel_parameters(&p1, &p2), expected);
1396
1397        let p1 = ChannelParameters {
1398            channel_mode: Some(fidl_bt::ChannelMode::EnhancedRetransmission),
1399            max_rx_sdu_size: None,
1400            security_requirements: None,
1401        };
1402        let p2 = ChannelParameters {
1403            channel_mode: Some(fidl_bt::ChannelMode::Basic),
1404            max_rx_sdu_size: Some(70),
1405            security_requirements: None,
1406        };
1407        let expected = ChannelParameters {
1408            channel_mode: Some(fidl_bt::ChannelMode::Basic),
1409            max_rx_sdu_size: Some(70),
1410            security_requirements: None,
1411        };
1412        assert_eq!(combine_channel_parameters(&p1, &p2), expected);
1413
1414        let empty_seq_reqs = SecurityRequirements::default();
1415        let p1 = ChannelParameters {
1416            channel_mode: None,
1417            max_rx_sdu_size: Some(75),
1418            security_requirements: Some(empty_seq_reqs.clone()),
1419        };
1420        let p2 = ChannelParameters {
1421            channel_mode: Some(fidl_bt::ChannelMode::EnhancedRetransmission),
1422            max_rx_sdu_size: None,
1423            security_requirements: None,
1424        };
1425        let expected = ChannelParameters {
1426            channel_mode: Some(fidl_bt::ChannelMode::EnhancedRetransmission),
1427            max_rx_sdu_size: Some(75),
1428            security_requirements: Some(empty_seq_reqs),
1429        };
1430        assert_eq!(combine_channel_parameters(&p1, &p2), expected);
1431
1432        let reqs1 = SecurityRequirements {
1433            authentication_required: Some(true),
1434            secure_connections_required: None,
1435        };
1436        let reqs2 = SecurityRequirements {
1437            authentication_required: Some(false),
1438            secure_connections_required: Some(false),
1439        };
1440        let combined_reqs = combine_security_requirements(&reqs1, &reqs2);
1441        let p1 = ChannelParameters {
1442            channel_mode: None,
1443            max_rx_sdu_size: Some(90),
1444            security_requirements: Some(reqs1),
1445        };
1446        let p2 = ChannelParameters {
1447            channel_mode: Some(fidl_bt::ChannelMode::Basic),
1448            max_rx_sdu_size: Some(70),
1449            security_requirements: Some(reqs2),
1450        };
1451        let expected = ChannelParameters {
1452            channel_mode: Some(fidl_bt::ChannelMode::Basic),
1453            max_rx_sdu_size: Some(70),
1454            security_requirements: Some(combined_reqs),
1455        };
1456        assert_eq!(combine_channel_parameters(&p1, &p2), expected);
1457    }
1458
1459    #[fuchsia::test]
1460    async fn local_sco_parameters_inspect_tree() {
1461        let inspect = inspect::Inspector::default();
1462        assert_data_tree!(inspect, root: {});
1463
1464        let params = fidl_bredr::ScoConnectionParameters {
1465            parameter_set: Some(fidl_bredr::HfpParameterSet::D1),
1466            air_coding_format: Some(fidl_bt::AssignedCodingFormat::Cvsd),
1467            air_frame_size: Some(60),
1468            io_bandwidth: Some(16000),
1469            io_coding_format: Some(fidl_bt::AssignedCodingFormat::LinearPcm),
1470            io_frame_size: Some(16),
1471            io_pcm_data_format: Some(fidl_fuchsia_hardware_audio::SampleFormat::PcmSigned),
1472            io_pcm_sample_payload_msb_position: Some(1),
1473            path: Some(fidl_bredr::DataPath::Offload),
1474            ..Default::default()
1475        };
1476
1477        let mut local: ValidScoConnectionParameters = params.try_into().expect("can convert");
1478        assert_data_tree!(inspect, root: {});
1479
1480        let _ = local.iattach(&inspect.root(), "state").expect("can attach inspect");
1481        assert_data_tree!(inspect, root: {
1482            state: {
1483                parameter_set: "D1",
1484                air_coding_format: "Cvsd",
1485                air_frame_size: 60u64,
1486                io_bandwidth: 16000u64,
1487                io_coding_format: "LinearPcm",
1488                io_frame_size: 16u64,
1489                io_pcm_data_format: "PcmSigned",
1490                io_pcm_sample_payload_msb_position: 1u64,
1491                path: "Offload",
1492            }
1493        });
1494    }
1495
1496    #[test]
1497    fn data_element_primitve_conversions() {
1498        type Result<T> = std::result::Result<T, DataElementConversionError>;
1499
1500        let rust_u8 = 8u8;
1501        let data_element_uint8 = DataElement::Uint8(8u8);
1502        let data_element_uint8_into: DataElement = rust_u8.into();
1503        let rust_u8_ok: Result<u8> = data_element_uint8.clone().try_into();
1504        let rust_u8_err: Result<u16> = data_element_uint8.clone().try_into();
1505        assert_eq!(data_element_uint8_into, data_element_uint8);
1506        assert_eq!(rust_u8_ok, Ok(rust_u8));
1507        assert_eq!(
1508            rust_u8_err,
1509            Err(DataElementConversionError { data_element: data_element_uint8 })
1510        );
1511
1512        let rust_i8 = 9i8;
1513        let data_element_int8 = DataElement::Int8(9i8);
1514        let data_element_int8_into: DataElement = rust_i8.into();
1515        let rust_i8_ok: Result<i8> = data_element_int8.clone().try_into();
1516        let rust_i8_err: Result<u16> = data_element_int8.clone().try_into();
1517        assert_eq!(data_element_int8_into, data_element_int8);
1518        assert_eq!(rust_i8_ok, Ok(rust_i8));
1519        assert_eq!(
1520            rust_i8_err,
1521            Err(DataElementConversionError { data_element: data_element_int8 })
1522        );
1523
1524        let rust_u16 = 16u16;
1525        let data_element_uint16 = DataElement::Uint16(16u16);
1526        let data_element_uint16_into: DataElement = rust_u16.into();
1527        let rust_u16_ok: Result<u16> = data_element_uint16.clone().try_into();
1528        let rust_u16_err: Result<i16> = data_element_uint16.clone().try_into();
1529        assert_eq!(data_element_uint16_into, data_element_uint16);
1530        assert_eq!(rust_u16_ok, Ok(rust_u16));
1531        assert_eq!(
1532            rust_u16_err,
1533            Err(DataElementConversionError { data_element: data_element_uint16 })
1534        );
1535
1536        let rust_i16 = 17i16;
1537        let data_element_int16 = DataElement::Int16(17i16);
1538        let data_element_int16_into: DataElement = rust_i16.into();
1539        let rust_i16_ok: Result<i16> = data_element_int16.clone().try_into();
1540        let rust_i16_err: Result<u16> = data_element_int16.clone().try_into();
1541        assert_eq!(data_element_int16_into, data_element_int16);
1542        assert_eq!(rust_i16_ok, Ok(rust_i16));
1543        assert_eq!(
1544            rust_i16_err,
1545            Err(DataElementConversionError { data_element: data_element_int16 })
1546        );
1547
1548        let rust_u32 = 32u32;
1549        let data_element_uint32 = DataElement::Uint32(32u32);
1550        let data_element_uint32_into: DataElement = rust_u32.into();
1551        let rust_u32_ok: Result<u32> = data_element_uint32.clone().try_into();
1552        let rust_u32_err: Result<u16> = data_element_uint32.clone().try_into();
1553        assert_eq!(data_element_uint32_into, data_element_uint32);
1554        assert_eq!(rust_u32_ok, Ok(rust_u32));
1555        assert_eq!(
1556            rust_u32_err,
1557            Err(DataElementConversionError { data_element: data_element_uint32 })
1558        );
1559
1560        let rust_i32 = 33i32;
1561        let data_element_int32 = DataElement::Int32(33i32);
1562        let data_element_int32_into: DataElement = rust_i32.into();
1563        let rust_i32_ok: Result<i32> = data_element_int32.clone().try_into();
1564        let rust_i32_err: Result<u16> = data_element_int32.clone().try_into();
1565        assert_eq!(data_element_int32_into, data_element_int32);
1566        assert_eq!(rust_i32_ok, Ok(rust_i32));
1567        assert_eq!(
1568            rust_i32_err,
1569            Err(DataElementConversionError { data_element: data_element_int32 })
1570        );
1571
1572        let rust_u64 = 64u64;
1573        let data_element_uint64 = DataElement::Uint64(64u64);
1574        let data_element_uint64_into: DataElement = rust_u64.into();
1575        let rust_u64_ok: Result<u64> = data_element_uint64.clone().try_into();
1576        let rust_u64_err: Result<u16> = data_element_uint64.clone().try_into();
1577        assert_eq!(data_element_uint64_into, data_element_uint64);
1578        assert_eq!(rust_u64_ok, Ok(rust_u64));
1579        assert_eq!(
1580            rust_u64_err,
1581            Err(DataElementConversionError { data_element: data_element_uint64 })
1582        );
1583
1584        let rust_i64 = 65i64;
1585        let data_element_int64 = DataElement::Int64(65i64);
1586        let data_element_int64_into: DataElement = rust_i64.into();
1587        let rust_i64_ok: Result<i64> = data_element_int64.clone().try_into();
1588        let rust_i64_err: Result<u16> = data_element_int64.clone().try_into();
1589        assert_eq!(data_element_int64_into, data_element_int64);
1590        assert_eq!(rust_i64_ok, Ok(rust_i64));
1591        assert_eq!(
1592            rust_i64_err,
1593            Err(DataElementConversionError { data_element: data_element_int64 })
1594        );
1595
1596        let rust_vec = "ABC".as_bytes().to_vec();
1597        let data_element_str = DataElement::Str("ABC".as_bytes().to_vec());
1598        let data_element_str_into: DataElement = rust_vec.clone().into();
1599        let rust_vec_ok: Result<Vec<u8>> = data_element_str.clone().try_into();
1600        let rust_vec_err: Result<u16> = data_element_str.clone().try_into();
1601        assert_eq!(data_element_str_into, data_element_str);
1602        assert_eq!(rust_vec_ok, Ok(rust_vec));
1603        assert_eq!(
1604            rust_vec_err,
1605            Err(DataElementConversionError { data_element: data_element_str })
1606        );
1607
1608        let rust_uuid: fidl_bt::Uuid = Uuid::new16(0x1101).into();
1609        let data_element_uuid = DataElement::Uuid(Uuid::new16(0x1101).into());
1610        let data_element_uuid_into: DataElement = rust_uuid.clone().into();
1611        let rust_uuid_ok: Result<fidl_bt::Uuid> = data_element_uuid.clone().try_into();
1612        let rust_uuid_err: Result<u16> = data_element_uuid.clone().try_into();
1613        assert_eq!(data_element_uuid_into, data_element_uuid);
1614        assert_eq!(rust_uuid_ok, Ok(rust_uuid));
1615        assert_eq!(
1616            rust_uuid_err,
1617            Err(DataElementConversionError { data_element: data_element_uuid })
1618        );
1619
1620        let rust_string = String::from("ABC");
1621        let data_element_url = DataElement::Url(String::from("ABC"));
1622        let data_element_url_into: DataElement = rust_string.clone().into();
1623        let rust_string_ok: Result<String> = data_element_url.clone().try_into();
1624        let rust_string_err: Result<u16> = data_element_url.clone().try_into();
1625        assert_eq!(data_element_url_into, data_element_url);
1626        assert_eq!(rust_string_ok, Ok(rust_string));
1627        assert_eq!(
1628            rust_string_err,
1629            Err(DataElementConversionError { data_element: data_element_url })
1630        );
1631
1632        let rust_bool = true;
1633        let data_element_bool = DataElement::Bool(true);
1634        let data_element_bool_into: DataElement = rust_bool.into();
1635        let rust_bool_ok: Result<bool> = data_element_bool.clone().try_into();
1636        let rust_bool_err: Result<u16> = data_element_bool.clone().try_into();
1637        assert_eq!(data_element_bool_into, data_element_bool);
1638        assert_eq!(rust_bool_ok, Ok(rust_bool));
1639        assert_eq!(
1640            rust_bool_err,
1641            Err(DataElementConversionError { data_element: data_element_bool })
1642        );
1643    }
1644
1645    #[test]
1646    fn test_find_service_class_uuids() {
1647        // No attributes -> empty vec.
1648        assert_eq!(find_all_service_classes(&[]), Vec::<Uuid>::new());
1649
1650        // No service class attribute -> empty vec.
1651        let attributes = vec![fidl_bredr::Attribute {
1652            id: Some(fidl_bredr::ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST),
1653            element: Some(fidl_bredr::DataElement::Sequence(vec![Some(Box::new(
1654                fidl_bredr::DataElement::Uuid(Uuid::new16(0x1101).into()),
1655            ))])),
1656            ..Default::default()
1657        }];
1658        assert_eq!(find_all_service_classes(&attributes), Vec::<Uuid>::new());
1659
1660        // Wrong element type for service class attribute -> empty vec.
1661        let attributes = vec![fidl_bredr::Attribute {
1662            id: Some(fidl_bredr::ATTR_SERVICE_CLASS_ID_LIST),
1663            element: Some(fidl_bredr::DataElement::Uint32(0xc0defae5u32)),
1664            ..Default::default()
1665        }];
1666        assert_eq!(find_all_service_classes(&attributes), Vec::<Uuid>::new());
1667
1668        // Valid attribute with a mix of elements -> returns only UUIDs.
1669        let uuid1 = Uuid::new16(0x1101);
1670        let uuid2 = Uuid::from_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
1671        let attribute = fidl_bredr::Attribute {
1672            id: Some(fidl_bredr::ATTR_SERVICE_CLASS_ID_LIST),
1673            element: Some(fidl_bredr::DataElement::Sequence(vec![
1674                Some(Box::new(fidl_bredr::DataElement::Uuid(uuid1.into()))),
1675                Some(Box::new(fidl_bredr::DataElement::Uint16(5))), // Not a UUID, should be ignored.
1676                Some(Box::new(fidl_bredr::DataElement::Uuid(uuid2.into()))),
1677                None, // Empty element, should be ignored.
1678            ])),
1679            ..Default::default()
1680        };
1681
1682        let result = find_all_service_classes(&[attribute]);
1683        assert_eq!(vec![uuid1, uuid2], result);
1684    }
1685
1686    #[test]
1687    fn test_channel_number_from_protocol() {
1688        // L2CAP service with valid PSM
1689        let l2cap_only = vec![fidl_bredr::ProtocolDescriptor {
1690            protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1691            params: Some(vec![fidl_bredr::DataElement::Uint16(42)]),
1692            ..Default::default()
1693        }];
1694        assert_eq!(channel_number_from_protocol(&l2cap_only).unwrap(), 42);
1695
1696        // RFCOMM with valid server channel number
1697        let rfcomm = vec![
1698            fidl_bredr::ProtocolDescriptor {
1699                protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1700                params: Some(vec![]), // No PSM for RFCOMM
1701                ..Default::default()
1702            },
1703            fidl_bredr::ProtocolDescriptor {
1704                protocol: Some(fidl_bredr::ProtocolIdentifier::Rfcomm),
1705                params: Some(vec![fidl_bredr::DataElement::Uint8(5)]),
1706                ..Default::default()
1707            },
1708        ];
1709        assert_eq!(channel_number_from_protocol(&rfcomm).unwrap(), 5);
1710
1711        // L2CAP with invalid params (empty) is an error
1712        let l2cap_invalid = vec![fidl_bredr::ProtocolDescriptor {
1713            protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1714            params: Some(vec![]),
1715            ..Default::default()
1716        }];
1717        assert!(channel_number_from_protocol(&l2cap_invalid).is_err());
1718
1719        // RFCOMM service with missing channel number is an error
1720        let rfcomm_invalid = vec![
1721            fidl_bredr::ProtocolDescriptor {
1722                protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1723                params: Some(vec![]),
1724                ..Default::default()
1725            },
1726            fidl_bredr::ProtocolDescriptor {
1727                protocol: Some(fidl_bredr::ProtocolIdentifier::Rfcomm),
1728                params: Some(vec![]),
1729                ..Default::default()
1730            },
1731        ];
1732        assert!(channel_number_from_protocol(&rfcomm_invalid).is_err());
1733
1734        // RFCOMM invalid channel type
1735        let rfcomm_wrong_type = vec![
1736            fidl_bredr::ProtocolDescriptor {
1737                protocol: Some(fidl_bredr::ProtocolIdentifier::L2Cap),
1738                params: Some(vec![]),
1739                ..Default::default()
1740            },
1741            fidl_bredr::ProtocolDescriptor {
1742                protocol: Some(fidl_bredr::ProtocolIdentifier::Rfcomm),
1743                params: Some(vec![fidl_bredr::DataElement::Uint16(5)]),
1744                ..Default::default()
1745            },
1746        ];
1747        assert!(channel_number_from_protocol(&rfcomm_wrong_type).is_err());
1748
1749        // Empty service is an error
1750        assert!(channel_number_from_protocol(&vec![]).is_err());
1751    }
1752
1753    #[test]
1754    fn test_channel_number_from_parameters() {
1755        // RFCOMM with valid channel
1756        let rfcomm = fidl_bredr::ConnectParameters::Rfcomm(fidl_bredr::RfcommParameters {
1757            channel: Some(7),
1758            ..Default::default()
1759        });
1760        assert_eq!(channel_number_from_parameters(&rfcomm).unwrap(), 7);
1761
1762        // L2CAP with valid PSM
1763        let l2cap = fidl_bredr::ConnectParameters::L2cap(fidl_bredr::L2capParameters {
1764            psm: Some(42),
1765            ..Default::default()
1766        });
1767        assert_eq!(channel_number_from_parameters(&l2cap).unwrap(), 42);
1768
1769        // RFCOMM missing channel
1770        let rfcomm_invalid = fidl_bredr::ConnectParameters::Rfcomm(fidl_bredr::RfcommParameters {
1771            channel: None,
1772            ..Default::default()
1773        });
1774        assert!(channel_number_from_parameters(&rfcomm_invalid).is_err());
1775
1776        // L2CAP missing PSM
1777        let l2cap_invalid = fidl_bredr::ConnectParameters::L2cap(fidl_bredr::L2capParameters {
1778            psm: None,
1779            ..Default::default()
1780        });
1781        assert!(channel_number_from_parameters(&l2cap_invalid).is_err());
1782    }
1783}