| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use super::types::{ |
| ConnectivityState, DeviceStateDto, MacAddressFilterSettingsDto, NeighborInfoDto, |
| }; |
| use crate::common_utils::lowpan_context::LowpanContext; |
| use anyhow::Error; |
| use fidl_fuchsia_lowpan_device::{DeviceExtraProxy, DeviceProxy}; |
| use fidl_fuchsia_lowpan_test::DeviceTestProxy; |
| use fuchsia_sync::RwLock; |
| |
| /// Perform Wpan FIDL operations. |
| /// |
| /// Note this object is shared among all threads created by server. |
| #[derive(Debug)] |
| pub struct WpanFacade { |
| /// The proxy to access the lowpan Device service. |
| device: RwLock<Option<DeviceProxy>>, |
| /// The proxy to access the lowpan DeviceTest service. |
| device_test: RwLock<Option<DeviceTestProxy>>, |
| /// The proxy to access the lowpan DeviceExtra service. |
| device_extra: RwLock<Option<DeviceExtraProxy>>, |
| } |
| |
| impl WpanFacade { |
| pub fn new() -> WpanFacade { |
| WpanFacade { |
| device: RwLock::new(None), |
| device_test: RwLock::new(None), |
| device_extra: RwLock::new(None), |
| } |
| } |
| |
| /// Returns the DeviceTestManager proxy provided on instantiation |
| /// or establishes a new connection. |
| pub async fn initialize_proxies(&self) -> Result<(), Error> { |
| let (device, device_extra, device_test) = match LowpanContext::new(None) { |
| Ok(low_pan_context) => low_pan_context.get_default_device_proxies().await?, |
| _ => bail!("Error retrieving default device proxies"), |
| }; |
| *self.device.write() = Some(device.clone()); |
| *self.device_extra.write() = Some(device_extra.clone()); |
| *self.device_test.write() = Some(device_test.clone()); |
| Ok(()) |
| } |
| |
| /// Returns the thread rloc from the DeviceTest proxy service. |
| pub async fn get_thread_rloc16(&self) -> Result<u16, Error> { |
| let thread_rloc16 = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_thread_rloc16().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(thread_rloc16) |
| } |
| |
| /// Returns the current mac address (thread random mac address) from the DeviceTest |
| /// proxy service. |
| pub async fn get_ncp_mac_address(&self) -> Result<[u8; 8], Error> { |
| let current_mac_address = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_current_mac_address().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(current_mac_address.octets) |
| } |
| |
| /// Returns the ncp channel from the DeviceTest proxy service. |
| pub async fn get_ncp_channel(&self) -> Result<u16, Error> { |
| let current_channel = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_current_channel().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(current_channel) |
| } |
| |
| /// Returns the current rssi from the DeviceTest proxy service. |
| pub async fn get_ncp_rssi(&self) -> Result<i32, Error> { |
| let ncp_rssi = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_current_rssi().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(ncp_rssi.into()) |
| } |
| |
| /// Returns the factory mac address from the DeviceTest proxy service. |
| pub async fn get_weave_node_id(&self) -> Result<[u8; 8], Error> { |
| let factory_mac_address = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_factory_mac_address().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(factory_mac_address.octets) |
| } |
| |
| /// Returns the network name from the DeviceExtra proxy service. |
| pub async fn get_network_name(&self) -> Result<Vec<u8>, Error> { |
| let raw_name = match self.device_extra.read().as_ref() { |
| Some(device_extra) => device_extra.watch_identity().await?.raw_name, |
| _ => bail!("DeviceExtra proxy is not set"), |
| }; |
| match raw_name { |
| Some(raw_name) => Ok(raw_name), |
| None => bail!("Network name is not specified!"), |
| } |
| } |
| |
| /// Returns the partition id from the DeviceTest proxy service. |
| pub async fn get_partition_id(&self) -> Result<u32, Error> { |
| let partition_id = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_partition_id().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(partition_id) |
| } |
| |
| /// Returns the thread router id from the DeviceTest proxy service. |
| pub async fn get_thread_router_id(&self) -> Result<u8, Error> { |
| let router_id = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_thread_router_id().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(router_id) |
| } |
| |
| /// Returns the device state from the DeviceTest proxy service. |
| pub async fn get_ncp_device_state(&self) -> Result<DeviceStateDto, Error> { |
| let device_state = match self.device.read().as_ref() { |
| Some(device) => device.watch_device_state().await?, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| Ok(device_state.into()) |
| } |
| |
| /// Returns the connectivity state from the DeviceTest proxy service. |
| pub async fn get_ncp_state(&self) -> Result<ConnectivityState, Error> { |
| let device_state = match self.device.read().as_ref() { |
| Some(device) => device.watch_device_state().await?.connectivity_state, |
| _ => bail!("DeviceTest proxy is not set"), |
| }; |
| match device_state { |
| Some(connectivity_state) => Ok(connectivity_state.into()), |
| None => bail!("Device state is not defined!"), |
| } |
| } |
| |
| /// Returns true if the connectivity state is commissioned. |
| pub async fn get_is_commissioned(&self) -> Result<bool, Error> { |
| let ncp_state = self.get_ncp_state().await?; |
| let is_commissioned = match ncp_state { |
| ConnectivityState::Attached |
| | ConnectivityState::Attaching |
| | ConnectivityState::Isolated |
| | ConnectivityState::Ready => true, |
| _ => false, |
| }; |
| Ok(is_commissioned) |
| } |
| |
| /// Returns the panid from the DeviceExtra proxy service. |
| pub async fn get_panid(&self) -> Result<u16, Error> { |
| match self.device_extra.read().as_ref() { |
| Some(device_extra) => match device_extra.watch_identity().await?.panid { |
| Some(panid) => Ok(panid), |
| None => bail!("Pan id is not specified!"), |
| }, |
| _ => bail!("DeviceExtra proxy is not set"), |
| } |
| } |
| |
| /// Returns the mac address filter settings from the DeviceTest proxy service. |
| pub async fn get_mac_address_filter_settings( |
| &self, |
| ) -> Result<MacAddressFilterSettingsDto, Error> { |
| let settings = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_mac_address_filter_settings().await?, |
| _ => bail!("DeviceTest proxy is not set!"), |
| }; |
| Ok(settings.into()) |
| } |
| |
| /// Replaces the mac address filter settings on the DeviceTest proxy service. |
| pub async fn replace_mac_address_filter_settings( |
| &self, |
| settings: MacAddressFilterSettingsDto, |
| ) -> Result<(), Error> { |
| match self.device_test.read().as_ref() { |
| Some(device_test) => { |
| device_test.replace_mac_address_filter_settings(&settings.into()).await? |
| } |
| None => bail!("DeviceTest proxy is not set!"), |
| } |
| Ok(()) |
| } |
| |
| ///Returns the thread neighbor table from the DeviceTest proxy service. |
| pub async fn get_neighbor_table(&self) -> Result<Vec<NeighborInfoDto>, Error> { |
| let settings = match self.device_test.read().as_ref() { |
| Some(device_test) => device_test.get_neighbor_table().await?, |
| _ => bail!("DeviceTest proxy is not set!"), |
| }; |
| Ok(settings.into_iter().map(|setting| setting.into()).collect()) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::wpan::types::{MacAddressFilterItemDto, MacAddressFilterModeDto}; |
| use fidl::endpoints::ProtocolMarker; |
| use fidl_fuchsia_lowpan_device::{DeviceExtraMarker, DeviceMarker}; |
| use fidl_fuchsia_lowpan_test::DeviceTestMarker; |
| use fuchsia_async as fasync; |
| use futures::prelude::*; |
| use lazy_static::lazy_static; |
| use lowpan_driver_common::DummyDevice; |
| use lowpan_driver_common::ServeTo; |
| |
| lazy_static! { |
| static ref MOCK_TESTER: MockTester = MockTester::new(); |
| } |
| |
| struct MockTester { |
| dummy_device: DummyDevice, |
| } |
| impl MockTester { |
| fn new() -> Self { |
| Self { dummy_device: DummyDevice::default() } |
| } |
| |
| fn create_endpoints<T: ProtocolMarker>() -> (RwLock<Option<T::Proxy>>, T::RequestStream) { |
| let (client_ep, server_ep) = fidl::endpoints::create_endpoints::<T>(); |
| (RwLock::new(Some(client_ep.into_proxy().unwrap())), server_ep.into_stream().unwrap()) |
| } |
| |
| pub fn create_facade_and_serve( |
| &'static self, |
| ) -> ( |
| WpanFacade, |
| ( |
| impl Future<Output = anyhow::Result<()>>, |
| impl Future<Output = anyhow::Result<()>>, |
| impl Future<Output = anyhow::Result<()>>, |
| ), |
| ) { |
| let (device_proxy, device_server) = MockTester::create_endpoints::<DeviceMarker>(); |
| let (device_test_proxy, device_test_server) = |
| MockTester::create_endpoints::<DeviceTestMarker>(); |
| let (device_extra_proxy, device_extra_server) = |
| MockTester::create_endpoints::<DeviceExtraMarker>(); |
| |
| let facade = WpanFacade { |
| device: device_proxy, |
| device_test: device_test_proxy, |
| device_extra: device_extra_proxy, |
| }; |
| |
| ( |
| facade, |
| ( |
| self.dummy_device.serve_to(device_server), |
| self.dummy_device.serve_to(device_test_server), |
| self.dummy_device.serve_to(device_extra_server), |
| ), |
| ) |
| } |
| |
| pub async fn assert_wpan_fn<TResult>( |
| func: impl Future<Output = Result<TResult, Error>>, |
| server_future: ( |
| impl Future<Output = anyhow::Result<()>>, |
| impl Future<Output = anyhow::Result<()>>, |
| impl Future<Output = anyhow::Result<()>>, |
| ), |
| ) { |
| let facade_fut = async move { |
| let awaiting = func.await; |
| awaiting.expect("No value returned!"); |
| }; |
| futures::select! { |
| err = server_future.0.fuse() => panic!("Server task stopped: {:?}", err), |
| err = server_future.1.fuse() => panic!("Server task stopped: {:?}", err), |
| err = server_future.2.fuse() => panic!("Server task stopped: {:?}", err), |
| _ = facade_fut.fuse() => (), |
| } |
| } |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_thread_rloc16() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_thread_rloc16(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_ncp_channel() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_ncp_channel(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_ncp_mac_address() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_ncp_mac_address(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_ncp_rssi() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_ncp_rssi(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_weave_node_id() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_weave_node_id(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_network_name() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_network_name(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_partition_id() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_partition_id(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_thread_router_id() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_thread_router_id(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_ncp_device_state() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_ncp_device_state(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_ncp_state() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_ncp_state(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_is_commissioned() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_is_commissioned(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_panid() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_panid(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_mac_address_filter_settings() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_mac_address_filter_settings(), facade.1).await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_replace_mac_address_filter_settings() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn( |
| facade.0.replace_mac_address_filter_settings(MacAddressFilterSettingsDto { |
| mode: Some(MacAddressFilterModeDto::Allow), |
| items: Some(vec![MacAddressFilterItemDto { |
| mac_address: Some([0, 1, 2, 3, 4, 5, 6, 7]), |
| rssi: None, |
| }]), |
| }), |
| facade.1, |
| ) |
| .await; |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn test_get_neigbor_table() { |
| let facade = MOCK_TESTER.create_facade_and_serve(); |
| MockTester::assert_wpan_fn(facade.0.get_neighbor_table(), facade.1).await; |
| } |
| } |