sl4f_lib/wpan/
facade.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 super::types::{
6    ConnectivityState, DeviceStateDto, MacAddressFilterSettingsDto, NeighborInfoDto,
7};
8use crate::common_utils::lowpan_context::LowpanContext;
9use anyhow::Error;
10use fidl_fuchsia_lowpan_device::{DeviceExtraProxy, DeviceProxy};
11use fidl_fuchsia_lowpan_test::DeviceTestProxy;
12use fuchsia_sync::RwLock;
13
14/// Perform Wpan FIDL operations.
15///
16/// Note this object is shared among all threads created by server.
17#[derive(Debug)]
18pub struct WpanFacade {
19    /// The proxy to access the lowpan Device service.
20    device: RwLock<Option<DeviceProxy>>,
21    /// The proxy to access the lowpan DeviceTest service.
22    device_test: RwLock<Option<DeviceTestProxy>>,
23    /// The proxy to access the lowpan DeviceExtra service.
24    device_extra: RwLock<Option<DeviceExtraProxy>>,
25}
26
27impl WpanFacade {
28    pub fn new() -> WpanFacade {
29        WpanFacade {
30            device: RwLock::new(None),
31            device_test: RwLock::new(None),
32            device_extra: RwLock::new(None),
33        }
34    }
35
36    /// Returns the DeviceTestManager proxy provided on instantiation
37    /// or establishes a new connection.
38    pub async fn initialize_proxies(&self) -> Result<(), Error> {
39        let (device, device_extra, device_test) = match LowpanContext::new(None) {
40            Ok(low_pan_context) => low_pan_context.get_default_device_proxies().await?,
41            _ => bail!("Error retrieving default device proxies"),
42        };
43        *self.device.write() = Some(device);
44        *self.device_extra.write() = Some(device_extra);
45        *self.device_test.write() = Some(device_test);
46        Ok(())
47    }
48
49    /// Returns the thread rloc from the DeviceTest proxy service.
50    pub async fn get_thread_rloc16(&self) -> Result<u16, Error> {
51        let thread_rloc16 = match self.device_test.read().as_ref() {
52            Some(device_test) => device_test.get_thread_rloc16().await?,
53            _ => bail!("DeviceTest proxy is not set"),
54        };
55        Ok(thread_rloc16)
56    }
57
58    /// Returns the current mac address (thread random mac address) from the DeviceTest
59    /// proxy service.
60    pub async fn get_ncp_mac_address(&self) -> Result<[u8; 8], Error> {
61        let current_mac_address = match self.device_test.read().as_ref() {
62            Some(device_test) => device_test.get_current_mac_address().await?,
63            _ => bail!("DeviceTest proxy is not set"),
64        };
65        Ok(current_mac_address.octets)
66    }
67
68    /// Returns the ncp channel from the DeviceTest proxy service.
69    pub async fn get_ncp_channel(&self) -> Result<u16, Error> {
70        let current_channel = match self.device_test.read().as_ref() {
71            Some(device_test) => device_test.get_current_channel().await?,
72            _ => bail!("DeviceTest proxy is not set"),
73        };
74        Ok(current_channel)
75    }
76
77    /// Returns the current rssi from the DeviceTest proxy service.
78    pub async fn get_ncp_rssi(&self) -> Result<i32, Error> {
79        let ncp_rssi = match self.device_test.read().as_ref() {
80            Some(device_test) => device_test.get_current_rssi().await?,
81            _ => bail!("DeviceTest proxy is not set"),
82        };
83        Ok(ncp_rssi.into())
84    }
85
86    /// Returns the factory mac address from the DeviceTest proxy service.
87    pub async fn get_weave_node_id(&self) -> Result<[u8; 8], Error> {
88        let factory_mac_address = match self.device_test.read().as_ref() {
89            Some(device_test) => device_test.get_factory_mac_address().await?,
90            _ => bail!("DeviceTest proxy is not set"),
91        };
92        Ok(factory_mac_address.octets)
93    }
94
95    /// Returns the network name from the DeviceExtra proxy service.
96    pub async fn get_network_name(&self) -> Result<Vec<u8>, Error> {
97        let raw_name = match self.device_extra.read().as_ref() {
98            Some(device_extra) => device_extra.watch_identity().await?.raw_name,
99            _ => bail!("DeviceExtra proxy is not set"),
100        };
101        match raw_name {
102            Some(raw_name) => Ok(raw_name),
103            None => bail!("Network name is not specified!"),
104        }
105    }
106
107    /// Returns the partition id from the DeviceTest proxy service.
108    pub async fn get_partition_id(&self) -> Result<u32, Error> {
109        let partition_id = match self.device_test.read().as_ref() {
110            Some(device_test) => device_test.get_partition_id().await?,
111            _ => bail!("DeviceTest proxy is not set"),
112        };
113        Ok(partition_id)
114    }
115
116    /// Returns the thread router id from the DeviceTest proxy service.
117    pub async fn get_thread_router_id(&self) -> Result<u8, Error> {
118        let router_id = match self.device_test.read().as_ref() {
119            Some(device_test) => device_test.get_thread_router_id().await?,
120            _ => bail!("DeviceTest proxy is not set"),
121        };
122        Ok(router_id)
123    }
124
125    /// Returns the device state from the DeviceTest proxy service.
126    pub async fn get_ncp_device_state(&self) -> Result<DeviceStateDto, Error> {
127        let device_state = match self.device.read().as_ref() {
128            Some(device) => device.watch_device_state().await?,
129            _ => bail!("DeviceTest proxy is not set"),
130        };
131        Ok(device_state.into())
132    }
133
134    /// Returns the connectivity state from the DeviceTest proxy service.
135    pub async fn get_ncp_state(&self) -> Result<ConnectivityState, Error> {
136        let device_state = match self.device.read().as_ref() {
137            Some(device) => device.watch_device_state().await?.connectivity_state,
138            _ => bail!("DeviceTest proxy is not set"),
139        };
140        match device_state {
141            Some(connectivity_state) => Ok(connectivity_state.into()),
142            None => bail!("Device state is not defined!"),
143        }
144    }
145
146    /// Returns true if the connectivity state is commissioned.
147    pub async fn get_is_commissioned(&self) -> Result<bool, Error> {
148        let ncp_state = self.get_ncp_state().await?;
149        let is_commissioned = match ncp_state {
150            ConnectivityState::Attached
151            | ConnectivityState::Attaching
152            | ConnectivityState::Isolated
153            | ConnectivityState::Ready => true,
154            _ => false,
155        };
156        Ok(is_commissioned)
157    }
158
159    /// Returns the panid from the DeviceExtra proxy service.
160    pub async fn get_panid(&self) -> Result<u16, Error> {
161        match self.device_extra.read().as_ref() {
162            Some(device_extra) => match device_extra.watch_identity().await?.panid {
163                Some(panid) => Ok(panid),
164                None => bail!("Pan id is not specified!"),
165            },
166            _ => bail!("DeviceExtra proxy is not set"),
167        }
168    }
169
170    /// Returns the mac address filter settings from the DeviceTest proxy service.
171    pub async fn get_mac_address_filter_settings(
172        &self,
173    ) -> Result<MacAddressFilterSettingsDto, Error> {
174        let settings = match self.device_test.read().as_ref() {
175            Some(device_test) => device_test.get_mac_address_filter_settings().await?,
176            _ => bail!("DeviceTest proxy is not set!"),
177        };
178        Ok(settings.into())
179    }
180
181    /// Replaces the mac address filter settings on the DeviceTest proxy service.
182    pub async fn replace_mac_address_filter_settings(
183        &self,
184        settings: MacAddressFilterSettingsDto,
185    ) -> Result<(), Error> {
186        match self.device_test.read().as_ref() {
187            Some(device_test) => {
188                device_test.replace_mac_address_filter_settings(&settings.into()).await?
189            }
190            None => bail!("DeviceTest proxy is not set!"),
191        }
192        Ok(())
193    }
194
195    ///Returns the thread neighbor table from the DeviceTest proxy service.
196    pub async fn get_neighbor_table(&self) -> Result<Vec<NeighborInfoDto>, Error> {
197        let settings = match self.device_test.read().as_ref() {
198            Some(device_test) => device_test.get_neighbor_table().await?,
199            _ => bail!("DeviceTest proxy is not set!"),
200        };
201        Ok(settings.into_iter().map(|setting| setting.into()).collect())
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use crate::wpan::types::{MacAddressFilterItemDto, MacAddressFilterModeDto};
209    use fidl::endpoints::ProtocolMarker;
210    use fidl_fuchsia_lowpan_device::{DeviceExtraMarker, DeviceMarker};
211    use fidl_fuchsia_lowpan_test::DeviceTestMarker;
212    use fuchsia_async as fasync;
213    use futures::prelude::*;
214    use lowpan_driver_common::{DummyDevice, ServeTo};
215    use std::sync::LazyLock;
216
217    static MOCK_TESTER: LazyLock<MockTester> = LazyLock::new(|| MockTester::new());
218
219    struct MockTester {
220        dummy_device: DummyDevice,
221    }
222    impl MockTester {
223        fn new() -> Self {
224            Self { dummy_device: DummyDevice::default() }
225        }
226
227        fn create_endpoints<T: ProtocolMarker>() -> (RwLock<Option<T::Proxy>>, T::RequestStream) {
228            let (client_ep, server_ep) = fidl::endpoints::create_endpoints::<T>();
229            (RwLock::new(Some(client_ep.into_proxy())), server_ep.into_stream())
230        }
231
232        pub fn create_facade_and_serve(
233            &'static self,
234        ) -> (
235            WpanFacade,
236            (
237                impl Future<Output = anyhow::Result<()>>,
238                impl Future<Output = anyhow::Result<()>>,
239                impl Future<Output = anyhow::Result<()>>,
240            ),
241        ) {
242            let (device_proxy, device_server) = MockTester::create_endpoints::<DeviceMarker>();
243            let (device_test_proxy, device_test_server) =
244                MockTester::create_endpoints::<DeviceTestMarker>();
245            let (device_extra_proxy, device_extra_server) =
246                MockTester::create_endpoints::<DeviceExtraMarker>();
247
248            let facade = WpanFacade {
249                device: device_proxy,
250                device_test: device_test_proxy,
251                device_extra: device_extra_proxy,
252            };
253
254            (
255                facade,
256                (
257                    self.dummy_device.serve_to(device_server),
258                    self.dummy_device.serve_to(device_test_server),
259                    self.dummy_device.serve_to(device_extra_server),
260                ),
261            )
262        }
263
264        pub async fn assert_wpan_fn<TResult>(
265            func: impl Future<Output = Result<TResult, Error>>,
266            server_future: (
267                impl Future<Output = anyhow::Result<()>>,
268                impl Future<Output = anyhow::Result<()>>,
269                impl Future<Output = anyhow::Result<()>>,
270            ),
271        ) {
272            let facade_fut = async move {
273                let awaiting = func.await;
274                awaiting.expect("No value returned!");
275            };
276            futures::select! {
277                err = server_future.0.fuse() => panic!("Server task stopped: {:?}", err),
278                err = server_future.1.fuse() => panic!("Server task stopped: {:?}", err),
279                err = server_future.2.fuse() => panic!("Server task stopped: {:?}", err),
280                _ = facade_fut.fuse() => (),
281            }
282        }
283    }
284
285    #[fasync::run_singlethreaded(test)]
286    async fn test_get_thread_rloc16() {
287        let facade = MOCK_TESTER.create_facade_and_serve();
288        MockTester::assert_wpan_fn(facade.0.get_thread_rloc16(), facade.1).await;
289    }
290
291    #[fasync::run_singlethreaded(test)]
292    async fn test_get_ncp_channel() {
293        let facade = MOCK_TESTER.create_facade_and_serve();
294        MockTester::assert_wpan_fn(facade.0.get_ncp_channel(), facade.1).await;
295    }
296
297    #[fasync::run_singlethreaded(test)]
298    async fn test_get_ncp_mac_address() {
299        let facade = MOCK_TESTER.create_facade_and_serve();
300        MockTester::assert_wpan_fn(facade.0.get_ncp_mac_address(), facade.1).await;
301    }
302
303    #[fasync::run_singlethreaded(test)]
304    async fn test_get_ncp_rssi() {
305        let facade = MOCK_TESTER.create_facade_and_serve();
306        MockTester::assert_wpan_fn(facade.0.get_ncp_rssi(), facade.1).await;
307    }
308
309    #[fasync::run_singlethreaded(test)]
310    async fn test_get_weave_node_id() {
311        let facade = MOCK_TESTER.create_facade_and_serve();
312        MockTester::assert_wpan_fn(facade.0.get_weave_node_id(), facade.1).await;
313    }
314
315    #[fasync::run_singlethreaded(test)]
316    async fn test_get_network_name() {
317        let facade = MOCK_TESTER.create_facade_and_serve();
318        MockTester::assert_wpan_fn(facade.0.get_network_name(), facade.1).await;
319    }
320
321    #[fasync::run_singlethreaded(test)]
322    async fn test_get_partition_id() {
323        let facade = MOCK_TESTER.create_facade_and_serve();
324        MockTester::assert_wpan_fn(facade.0.get_partition_id(), facade.1).await;
325    }
326
327    #[fasync::run_singlethreaded(test)]
328    async fn test_get_thread_router_id() {
329        let facade = MOCK_TESTER.create_facade_and_serve();
330        MockTester::assert_wpan_fn(facade.0.get_thread_router_id(), facade.1).await;
331    }
332
333    #[fasync::run_singlethreaded(test)]
334    async fn test_get_ncp_device_state() {
335        let facade = MOCK_TESTER.create_facade_and_serve();
336        MockTester::assert_wpan_fn(facade.0.get_ncp_device_state(), facade.1).await;
337    }
338
339    #[fasync::run_singlethreaded(test)]
340    async fn test_get_ncp_state() {
341        let facade = MOCK_TESTER.create_facade_and_serve();
342        MockTester::assert_wpan_fn(facade.0.get_ncp_state(), facade.1).await;
343    }
344
345    #[fasync::run_singlethreaded(test)]
346    async fn test_get_is_commissioned() {
347        let facade = MOCK_TESTER.create_facade_and_serve();
348        MockTester::assert_wpan_fn(facade.0.get_is_commissioned(), facade.1).await;
349    }
350
351    #[fasync::run_singlethreaded(test)]
352    async fn test_get_panid() {
353        let facade = MOCK_TESTER.create_facade_and_serve();
354        MockTester::assert_wpan_fn(facade.0.get_panid(), facade.1).await;
355    }
356
357    #[fasync::run_singlethreaded(test)]
358    async fn test_get_mac_address_filter_settings() {
359        let facade = MOCK_TESTER.create_facade_and_serve();
360        MockTester::assert_wpan_fn(facade.0.get_mac_address_filter_settings(), facade.1).await;
361    }
362
363    #[fasync::run_singlethreaded(test)]
364    async fn test_replace_mac_address_filter_settings() {
365        let facade = MOCK_TESTER.create_facade_and_serve();
366        MockTester::assert_wpan_fn(
367            facade.0.replace_mac_address_filter_settings(MacAddressFilterSettingsDto {
368                mode: Some(MacAddressFilterModeDto::Allow),
369                items: Some(vec![MacAddressFilterItemDto {
370                    mac_address: Some([0, 1, 2, 3, 4, 5, 6, 7]),
371                    rssi: None,
372                }]),
373            }),
374            facade.1,
375        )
376        .await;
377    }
378
379    #[fasync::run_singlethreaded(test)]
380    async fn test_get_neigbor_table() {
381        let facade = MOCK_TESTER.create_facade_and_serve();
382        MockTester::assert_wpan_fn(facade.0.get_neighbor_table(), facade.1).await;
383    }
384}