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(false),
58            local_name: src.local_name.clone(),
59            discoverable: src.discoverable.unwrap_or(false),
60            discovering: src.discovering.unwrap_or(false),
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, fuchsia_inspect as inspect};
174
175    #[test]
176    fn from_fidl_id_not_present() {
177        let info = HostInfo::try_from(fsys::HostInfo::default());
178        assert!(info.is_err());
179    }
180
181    #[test]
182    fn from_fidl_technology_not_present() {
183        let info = fsys::HostInfo { id: Some(fbt::HostId { value: 1 }), ..Default::default() };
184        let info = HostInfo::try_from(info);
185        assert!(info.is_err());
186    }
187
188    #[test]
189    fn from_fidl_addresses_not_present() {
190        let info = fsys::HostInfo {
191            id: Some(fbt::HostId { value: 1 }),
192            technology: Some(fsys::TechnologyType::LowEnergy),
193            ..Default::default()
194        };
195        let info = HostInfo::try_from(info);
196        assert!(info.is_err());
197    }
198
199    #[test]
200    fn from_fidl_addresses_is_empty() {
201        let info = fsys::HostInfo {
202            id: Some(fbt::HostId { value: 1 }),
203            technology: Some(fsys::TechnologyType::LowEnergy),
204            addresses: Some(vec![]),
205            ..Default::default()
206        };
207        let info = HostInfo::try_from(info);
208        assert!(info.is_err());
209    }
210
211    #[test]
212    fn from_fidl_optional_fields_not_present() {
213        let info = fsys::HostInfo {
214            id: Some(fbt::HostId { value: 1 }),
215            technology: Some(fsys::TechnologyType::LowEnergy),
216            addresses: Some(vec![fbt::Address {
217                type_: fbt::AddressType::Public,
218                bytes: [1, 2, 3, 4, 5, 6],
219            }]),
220            ..Default::default()
221        };
222        let expected = HostInfo {
223            id: HostId(1),
224            technology: fsys::TechnologyType::LowEnergy,
225            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
226            active: false,
227            local_name: None,
228            discoverable: false,
229            discovering: false,
230        };
231
232        let info = HostInfo::try_from(info).expect("expected successful conversion");
233        assert_eq!(expected, info);
234    }
235
236    #[test]
237    fn from_fidl_optional_fields_present() {
238        let info = fsys::HostInfo {
239            id: Some(fbt::HostId { value: 1 }),
240            technology: Some(fsys::TechnologyType::LowEnergy),
241            active: Some(true),
242            local_name: Some("name".to_string()),
243            discoverable: Some(false),
244            discovering: Some(true),
245            addresses: Some(vec![fbt::Address {
246                type_: fbt::AddressType::Public,
247                bytes: [1, 2, 3, 4, 5, 6],
248            }]),
249            ..Default::default()
250        };
251        let expected = HostInfo {
252            id: HostId(1),
253            technology: fsys::TechnologyType::LowEnergy,
254            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
255            active: true,
256            local_name: Some("name".to_string()),
257            discoverable: false,
258            discovering: true,
259        };
260
261        let info = HostInfo::try_from(info).expect("expected successful conversion");
262        assert_eq!(expected, info);
263    }
264
265    #[test]
266    fn to_fidl() {
267        let info = HostInfo {
268            id: HostId(1),
269            technology: fsys::TechnologyType::LowEnergy,
270            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
271            active: false,
272            local_name: Some("name".to_string()),
273            discoverable: false,
274            discovering: false,
275        };
276        let expected = fsys::HostInfo {
277            id: Some(fbt::HostId { value: 1 }),
278            technology: Some(fsys::TechnologyType::LowEnergy),
279            active: Some(false),
280            local_name: Some("name".to_string()),
281            discoverable: Some(false),
282            discovering: Some(false),
283            addresses: Some(vec![fbt::Address {
284                type_: fbt::AddressType::Public,
285                bytes: [1, 2, 3, 4, 5, 6],
286            }]),
287            ..Default::default()
288        };
289
290        assert_eq!(expected, info.into());
291    }
292
293    #[fuchsia::test]
294    async fn inspect() {
295        let inspector = inspect::Inspector::default();
296        let node = inspector.root().create_child("info");
297        let info = HostInfo {
298            id: HostId(1),
299            technology: fsys::TechnologyType::LowEnergy,
300            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
301            active: false,
302            local_name: Some("name".to_string()),
303            discoverable: false,
304            discovering: true,
305        };
306        let mut info = Inspectable::new(info, node);
307        assert_data_tree!(inspector, root: {
308            info: contains {
309                identifier: 1u64,
310                technology: "LowEnergy",
311                active: 0u64,
312                discoverable: 0u64,
313                discovering: 1u64
314            }
315        });
316
317        info.update(HostInfo {
318            id: HostId(1),
319            technology: fsys::TechnologyType::LowEnergy,
320            addresses: vec![Address::Public([1, 2, 3, 4, 5, 6])],
321            active: true,
322            local_name: Some("foo".to_string()),
323            discoverable: true,
324            discovering: true,
325        });
326        assert_data_tree!(inspector, root: {
327            info: contains {
328                identifier: 1u64,
329                technology: "LowEnergy",
330                active: 1u64,
331                discoverable: 1u64,
332                discovering: 1u64
333            }
334        });
335    }
336}