Skip to main content

fuchsia_bluetooth/types/
host_info.rs

1// Copyright 2019 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_sys as fsys;
6#[cfg(target_os = "fuchsia")]
7use fuchsia_inspect::{self as inspect, Property};
8use std::fmt;
9
10use crate::error::Error;
11#[cfg(target_os = "fuchsia")]
12use crate::inspect::{DebugExt, InspectData, Inspectable, IsInspectable, ToProperty};
13use crate::types::{Address, HostId, addresses_to_custom_string};
14
15/// `HostInfo` contains informational parameters and state for a bt-host device.
16#[derive(Clone, Debug, PartialEq)]
17pub struct HostInfo {
18    /// Uniquely identifies a host on the current system.
19    pub id: HostId,
20
21    /// The Bluetooth technologies that are supported by this adapter.
22    pub technology: fsys::TechnologyType,
23
24    /// The known Classic and LE addresses associated with this Host.
25    /// This is guaranteed to be nonempty. The Public Address is always first.
26    pub addresses: Vec<Address>,
27
28    /// Indicates whether or not this is the active host. The system has one active host which
29    /// handles all Bluetooth procedures.
30    pub active: bool,
31
32    /// The local name of this host. This is the name that is visible to other devices when this
33    /// host is in the discoverable mode. Not present if the local device name is unknown.
34    pub local_name: Option<String>,
35
36    /// Whether or not the local adapter is currently discoverable over BR/EDR and
37    /// LE physical channels.
38    pub discoverable: bool,
39
40    /// Whether or not device discovery is currently being performed.
41    pub discovering: bool,
42}
43
44impl TryFrom<&fsys::HostInfo> for HostInfo {
45    type Error = Error;
46    fn try_from(src: &fsys::HostInfo) -> Result<HostInfo, Self::Error> {
47        let addresses =
48            src.addresses.as_ref().ok_or_else(|| Error::missing("HostInfo.addresses"))?;
49        if addresses.is_empty() {
50            return Err(Error::conversion("HostInfo.addresses must be nonempty"));
51        }
52        let addresses = addresses.iter().map(Into::into).collect();
53        Ok(HostInfo {
54            id: HostId::from(src.id.ok_or_else(|| Error::missing("HostInfo.id"))?),
55            technology: src.technology.ok_or_else(|| Error::missing("HostInfo.technology"))?,
56            addresses,
57            active: src.active.unwrap_or_default(),
58            local_name: src.local_name.clone(),
59            discoverable: src.discoverable.unwrap_or_default(),
60            discovering: src.discovering.unwrap_or_default(),
61        })
62    }
63}
64
65impl TryFrom<fsys::HostInfo> for HostInfo {
66    type Error = Error;
67    fn try_from(src: fsys::HostInfo) -> Result<HostInfo, Self::Error> {
68        HostInfo::try_from(&src)
69    }
70}
71
72impl From<&HostInfo> for fsys::HostInfo {
73    fn from(src: &HostInfo) -> fsys::HostInfo {
74        fsys::HostInfo {
75            id: Some(src.id.into()),
76            technology: Some(src.technology),
77            active: Some(src.active),
78            local_name: src.local_name.clone(),
79            discoverable: Some(src.discoverable),
80            discovering: Some(src.discovering),
81            addresses: Some(src.addresses.iter().map(Into::into).collect()),
82            ..Default::default()
83        }
84    }
85}
86
87impl From<HostInfo> for fsys::HostInfo {
88    fn from(src: HostInfo) -> fsys::HostInfo {
89        fsys::HostInfo::from(&src)
90    }
91}
92
93impl fmt::Display for HostInfo {
94    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
95        writeln!(fmt, "HostInfo:")?;
96        writeln!(fmt, "\tidentifier:\t{}", self.id)?;
97        writeln!(fmt, "\taddresses:\t{}", addresses_to_custom_string(&self.addresses, "\n\t\t\t"))?;
98        writeln!(fmt, "\tactive:\t{}", self.active)?;
99        writeln!(fmt, "\ttechnology:\t{:?}", self.technology)?;
100        writeln!(fmt, "\tlocal name:\t{}", self.local_name.as_deref().unwrap_or("(unknown)"))?;
101        writeln!(fmt, "\tdiscoverable:\t{}", self.discoverable)?;
102        writeln!(fmt, "\tdiscovering:\t{}", self.discovering)
103    }
104}
105
106#[cfg(target_os = "fuchsia")]
107impl Inspectable<HostInfo> {
108    pub fn update(&mut self, info: HostInfo) {
109        self.inspect.update(&info);
110        self.inner = info;
111    }
112}
113
114#[cfg(target_os = "fuchsia")]
115pub struct HostInfoInspect {
116    _inspect: inspect::Node,
117    identifier: inspect::UintProperty,
118    technology: inspect::StringProperty,
119    active: inspect::UintProperty,
120    discoverable: inspect::UintProperty,
121    discovering: inspect::UintProperty,
122}
123
124#[cfg(target_os = "fuchsia")]
125impl HostInfoInspect {
126    fn update(&mut self, info: &HostInfo) {
127        self.identifier.set(info.id.0);
128        self.technology.set(&info.technology.debug());
129        self.active.set(info.active.to_property());
130        self.discoverable.set(info.discoverable.to_property());
131        self.discovering.set(info.discovering.to_property());
132    }
133}
134
135#[cfg(target_os = "fuchsia")]
136impl IsInspectable for HostInfo {
137    type I = HostInfoInspect;
138}
139
140#[cfg(target_os = "fuchsia")]
141impl InspectData<HostInfo> for HostInfoInspect {
142    fn new(info: &HostInfo, inspect: inspect::Node) -> HostInfoInspect {
143        HostInfoInspect {
144            identifier: inspect.create_uint("identifier", info.id.0),
145            technology: inspect.create_string("technology", info.technology.debug()),
146            active: inspect.create_uint("active", info.active.to_property()),
147            discoverable: inspect.create_uint("discoverable", info.discoverable.to_property()),
148            discovering: inspect.create_uint("discovering", info.discovering.to_property()),
149            _inspect: inspect,
150        }
151    }
152}
153
154/// Example Bluetooth host for testing.
155pub fn example_host(id: HostId, active: bool, discoverable: bool) -> fsys::HostInfo {
156    fsys::HostInfo {
157        id: Some(id.into()),
158        technology: Some(fsys::TechnologyType::LowEnergy),
159        active: Some(active),
160        local_name: Some("fuchsia123".to_string()),
161        discoverable: Some(discoverable),
162        discovering: Some(true),
163        addresses: Some(vec![Address::Public([1, 2, 3, 4, 5, 6]).into()]),
164        ..Default::default()
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    use diagnostics_assertions::assert_data_tree;
173    use fidl_fuchsia_bluetooth as fbt;
174    use fuchsia_inspect as inspect;
175
176    #[test]
177    fn from_fidl_id_not_present() {
178        let info = HostInfo::try_from(fsys::HostInfo::default());
179        assert!(info.is_err());
180    }
181
182    #[test]
183    fn from_fidl_technology_not_present() {
184        let info = fsys::HostInfo { id: Some(fbt::HostId { value: 1 }), ..Default::default() };
185        let info = HostInfo::try_from(info);
186        assert!(info.is_err());
187    }
188
189    #[test]
190    fn from_fidl_addresses_not_present() {
191        let info = fsys::HostInfo {
192            id: Some(fbt::HostId { value: 1 }),
193            technology: Some(fsys::TechnologyType::LowEnergy),
194            ..Default::default()
195        };
196        let info = HostInfo::try_from(info);
197        assert!(info.is_err());
198    }
199
200    #[test]
201    fn from_fidl_addresses_is_empty() {
202        let info = fsys::HostInfo {
203            id: Some(fbt::HostId { value: 1 }),
204            technology: Some(fsys::TechnologyType::LowEnergy),
205            addresses: Some(vec![]),
206            ..Default::default()
207        };
208        let info = HostInfo::try_from(info);
209        assert!(info.is_err());
210    }
211
212    #[test]
213    fn from_fidl_optional_fields_not_present() {
214        let info = fsys::HostInfo {
215            id: Some(fbt::HostId { value: 1 }),
216            technology: Some(fsys::TechnologyType::LowEnergy),
217            addresses: Some(vec![fbt::Address {
218                type_: fbt::AddressType::Public,
219                bytes: [1, 2, 3, 4, 5, 6],
220            }]),
221            ..Default::default()
222        };
223        let expected = HostInfo {
224            id: HostId(1),
225            technology: fsys::TechnologyType::LowEnergy,
226            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
227            active: false,
228            local_name: None,
229            discoverable: false,
230            discovering: false,
231        };
232
233        let info = HostInfo::try_from(info).expect("expected successful conversion");
234        assert_eq!(expected, info);
235    }
236
237    #[test]
238    fn from_fidl_optional_fields_present() {
239        let info = fsys::HostInfo {
240            id: Some(fbt::HostId { value: 1 }),
241            technology: Some(fsys::TechnologyType::LowEnergy),
242            active: Some(true),
243            local_name: Some("name".to_string()),
244            discoverable: Some(false),
245            discovering: Some(true),
246            addresses: Some(vec![fbt::Address {
247                type_: fbt::AddressType::Public,
248                bytes: [1, 2, 3, 4, 5, 6],
249            }]),
250            ..Default::default()
251        };
252        let expected = HostInfo {
253            id: HostId(1),
254            technology: fsys::TechnologyType::LowEnergy,
255            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
256            active: true,
257            local_name: Some("name".to_string()),
258            discoverable: false,
259            discovering: true,
260        };
261
262        let info = HostInfo::try_from(info).expect("expected successful conversion");
263        assert_eq!(expected, info);
264    }
265
266    #[test]
267    fn to_fidl() {
268        let info = HostInfo {
269            id: HostId(1),
270            technology: fsys::TechnologyType::LowEnergy,
271            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
272            active: false,
273            local_name: Some("name".to_string()),
274            discoverable: false,
275            discovering: false,
276        };
277        let expected = fsys::HostInfo {
278            id: Some(fbt::HostId { value: 1 }),
279            technology: Some(fsys::TechnologyType::LowEnergy),
280            active: Some(false),
281            local_name: Some("name".to_string()),
282            discoverable: Some(false),
283            discovering: Some(false),
284            addresses: Some(vec![fbt::Address {
285                type_: fbt::AddressType::Public,
286                bytes: [1, 2, 3, 4, 5, 6],
287            }]),
288            ..Default::default()
289        };
290
291        assert_eq!(expected, info.into());
292    }
293
294    #[fuchsia::test]
295    async fn inspect() {
296        let inspector = inspect::Inspector::default();
297        let node = inspector.root().create_child("info");
298        let info = HostInfo {
299            id: HostId(1),
300            technology: fsys::TechnologyType::LowEnergy,
301            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
302            active: false,
303            local_name: Some("name".to_string()),
304            discoverable: false,
305            discovering: true,
306        };
307        let mut info = Inspectable::new(info, node);
308        assert_data_tree!(inspector, root: {
309            info: contains {
310                identifier: 1u64,
311                technology: "LowEnergy",
312                active: 0u64,
313                discoverable: 0u64,
314                discovering: 1u64
315            }
316        });
317
318        info.update(HostInfo {
319            id: HostId(1),
320            technology: fsys::TechnologyType::LowEnergy,
321            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
322            active: true,
323            local_name: Some("foo".to_string()),
324            discoverable: true,
325            discovering: true,
326        });
327        assert_data_tree!(inspector, root: {
328            info: contains {
329                identifier: 1u64,
330                technology: "LowEnergy",
331                active: 1u64,
332                discoverable: 1u64,
333                discovering: 1u64
334            }
335        });
336    }
337}