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