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