Skip to main content

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