1 // Copyright 2022, The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! This module offers a synchornized interface at UCI level. 16 //! 17 //! The module is designed with the replacement for Android UCI JNI adaptation in mind. The handling 18 //! of UciNotifications is different in UciManager and UciManagerSyncImpl as the sync version has 19 //! its behavior aligned with the Android JNI UCI, and routes the UciNotifications to 20 //! NotificationManager. 21 22 use log::{debug, error}; 23 use tokio::runtime::{Builder as RuntimeBuilder, Handle}; 24 use tokio::sync::mpsc; 25 use tokio::task; 26 27 use crate::error::{Error, Result}; 28 use crate::params::{ 29 AndroidRadarConfigResponse, AppConfigTlv, AppConfigTlvType, CapTlv, CoreSetConfigResponse, 30 CountryCode, DeviceConfigId, DeviceConfigTlv, GetDeviceInfoResponse, PowerStats, 31 RadarConfigTlv, RadarConfigTlvType, RawUciMessage, ResetConfig, SessionId, SessionState, 32 SessionType, SessionUpdateControllerMulticastResponse, SessionUpdateDtTagRangingRoundsResponse, 33 SetAppConfigResponse, UpdateMulticastListAction, UpdateTime, 34 }; 35 #[cfg(any(test, feature = "mock-utils"))] 36 use crate::uci::mock_uci_manager::MockUciManager; 37 use crate::uci::notification::{ 38 CoreNotification, DataRcvNotification, RadarDataRcvNotification, SessionNotification, 39 }; 40 use crate::uci::uci_hal::UciHal; 41 use crate::uci::uci_logger::{UciLogger, UciLoggerMode}; 42 use crate::uci::uci_manager::{UciManager, UciManagerImpl}; 43 use uwb_uci_packets::{ControleePhaseList, Controlees, PhaseList}; 44 45 /// The NotificationManager processes UciNotification relayed from UciManagerSync in a sync fashion. 46 /// The UciManagerSync assumes the NotificationManager takes the responsibility to properly handle 47 /// the notifications, including tracking the state of HAL. UciManagerSync and lower levels only 48 /// redirect and categorize the notifications. The notifications are processed through callbacks. 49 /// NotificationManager can be !Send and !Sync, as interfacing with other programs may require. 50 pub trait NotificationManager: 'static { 51 /// Callback for CoreNotification. on_core_notification(&mut self, core_notification: CoreNotification) -> Result<()>52 fn on_core_notification(&mut self, core_notification: CoreNotification) -> Result<()>; 53 54 /// Callback for SessionNotification. on_session_notification(&mut self, session_notification: SessionNotification) -> Result<()>55 fn on_session_notification(&mut self, session_notification: SessionNotification) -> Result<()>; 56 57 /// Callback for RawUciMessage. on_vendor_notification(&mut self, vendor_notification: RawUciMessage) -> Result<()>58 fn on_vendor_notification(&mut self, vendor_notification: RawUciMessage) -> Result<()>; 59 60 /// Callback for DataRcvNotification. on_data_rcv_notification( &mut self, data_rcv_notification: DataRcvNotification, ) -> Result<()>61 fn on_data_rcv_notification( 62 &mut self, 63 data_rcv_notification: DataRcvNotification, 64 ) -> Result<()>; 65 66 /// Callback for RadarDataRcvNotification. on_radar_data_rcv_notification( &mut self, radar_data_rcv_notification: RadarDataRcvNotification, ) -> Result<()>67 fn on_radar_data_rcv_notification( 68 &mut self, 69 radar_data_rcv_notification: RadarDataRcvNotification, 70 ) -> Result<()>; 71 } 72 73 /// Builder for NotificationManager. Builder is sent between threads. 74 pub trait NotificationManagerBuilder: 'static + Send + Sync { 75 /// Type of NotificationManager built. 76 type NotificationManager: NotificationManager; 77 /// Builds NotificationManager. The build operation Consumes Builder. build(self) -> Option<Self::NotificationManager>78 fn build(self) -> Option<Self::NotificationManager>; 79 } 80 81 struct NotificationDriver<U: NotificationManager> { 82 core_notification_receiver: mpsc::UnboundedReceiver<CoreNotification>, 83 session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, 84 vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, 85 data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, 86 radar_data_rcv_notification_receiver: mpsc::UnboundedReceiver<RadarDataRcvNotification>, 87 notification_manager: U, 88 } 89 impl<U: NotificationManager> NotificationDriver<U> { new( core_notification_receiver: mpsc::UnboundedReceiver<CoreNotification>, session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, radar_data_rcv_notification_receiver: mpsc::UnboundedReceiver<RadarDataRcvNotification>, notification_manager: U, ) -> Self90 fn new( 91 core_notification_receiver: mpsc::UnboundedReceiver<CoreNotification>, 92 session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, 93 vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, 94 data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, 95 radar_data_rcv_notification_receiver: mpsc::UnboundedReceiver<RadarDataRcvNotification>, 96 notification_manager: U, 97 ) -> Self { 98 Self { 99 core_notification_receiver, 100 session_notification_receiver, 101 vendor_notification_receiver, 102 data_rcv_notification_receiver, 103 radar_data_rcv_notification_receiver, 104 notification_manager, 105 } 106 } run(&mut self)107 async fn run(&mut self) { 108 loop { 109 tokio::select! { 110 Some(ntf) = self.core_notification_receiver.recv() =>{ 111 self.notification_manager.on_core_notification(ntf).unwrap_or_else(|e|{ 112 error!("NotificationDriver: CoreNotification callback error: {:?}",e); 113 }); 114 } 115 Some(ntf) = self.session_notification_receiver.recv() =>{ 116 self.notification_manager.on_session_notification(ntf).unwrap_or_else(|e|{ 117 error!("NotificationDriver: SessionNotification callback error: {:?}",e); 118 }); 119 } 120 Some(ntf) = self.vendor_notification_receiver.recv() =>{ 121 self.notification_manager.on_vendor_notification(ntf).unwrap_or_else(|e|{ 122 error!("NotificationDriver: RawUciMessage callback error: {:?}",e); 123 }); 124 } 125 Some(data) = self.data_rcv_notification_receiver.recv() =>{ 126 self.notification_manager.on_data_rcv_notification(data).unwrap_or_else(|e|{ 127 error!("NotificationDriver: OnDataRcv callback error: {:?}",e); 128 }); 129 } 130 Some(data) = self.radar_data_rcv_notification_receiver.recv() =>{ 131 self.notification_manager.on_radar_data_rcv_notification(data).unwrap_or_else(|e|{ 132 error!("NotificationDriver: OnRadarDataRcv callback error: {:?}",e); 133 }); 134 } 135 else =>{ 136 debug!("NotificationDriver dropping."); 137 break; 138 } 139 } 140 } 141 } 142 } 143 144 /// The UciManagerSync provides a synchornized version of UciManager. 145 /// 146 /// Note the processing of UciNotification is different: 147 /// set_X_notification_sender methods are removed. Instead, the method 148 /// redirect_notification(NotificationManagerBuilder) is introduced to avoid the 149 /// exposure of async tokio::mpsc. 150 pub struct UciManagerSync<U: UciManager> { 151 runtime_handle: Handle, 152 uci_manager: U, 153 } 154 impl<U: UciManager> UciManagerSync<U> { 155 /// Redirects notification to a new NotificationManager using the notification_manager_builder. 156 /// The NotificationManager will live on a separate thread. redirect_notification<T: NotificationManagerBuilder>( &mut self, notification_manager_builder: T, ) -> Result<()>157 pub fn redirect_notification<T: NotificationManagerBuilder>( 158 &mut self, 159 notification_manager_builder: T, 160 ) -> Result<()> { 161 let (core_notification_sender, core_notification_receiver) = 162 mpsc::unbounded_channel::<CoreNotification>(); 163 let (session_notification_sender, session_notification_receiver) = 164 mpsc::unbounded_channel::<SessionNotification>(); 165 let (vendor_notification_sender, vendor_notification_receiver) = 166 mpsc::unbounded_channel::<RawUciMessage>(); 167 let (data_rcv_notification_sender, data_rcv_notification_receiver) = 168 mpsc::unbounded_channel::<DataRcvNotification>(); 169 let (radar_data_rcv_notification_sender, radar_data_rcv_notification_receiver) = 170 mpsc::unbounded_channel::<RadarDataRcvNotification>(); 171 self.runtime_handle.to_owned().block_on(async { 172 self.uci_manager.set_core_notification_sender(core_notification_sender).await; 173 self.uci_manager.set_session_notification_sender(session_notification_sender).await; 174 self.uci_manager.set_vendor_notification_sender(vendor_notification_sender).await; 175 self.uci_manager.set_data_rcv_notification_sender(data_rcv_notification_sender).await; 176 self.uci_manager 177 .set_radar_data_rcv_notification_sender(radar_data_rcv_notification_sender) 178 .await; 179 }); 180 // The potentially !Send NotificationManager is created in a separate thread. 181 let (driver_status_sender, mut driver_status_receiver) = mpsc::unbounded_channel::<bool>(); 182 std::thread::spawn(move || { 183 let notification_runtime = 184 match RuntimeBuilder::new_current_thread().enable_all().build() { 185 Ok(nr) => nr, 186 Err(_) => { 187 // unwrap safe since receiver is in scope 188 driver_status_sender.send(false).unwrap(); 189 return; 190 } 191 }; 192 193 let local = task::LocalSet::new(); 194 let notification_manager = match notification_manager_builder.build() { 195 Some(nm) => { 196 // unwrap safe since receiver is in scope 197 driver_status_sender.send(true).unwrap(); 198 nm 199 } 200 None => { 201 // unwrap safe since receiver is in scope 202 driver_status_sender.send(false).unwrap(); 203 return; 204 } 205 }; 206 let mut notification_driver = NotificationDriver::new( 207 core_notification_receiver, 208 session_notification_receiver, 209 vendor_notification_receiver, 210 data_rcv_notification_receiver, 211 radar_data_rcv_notification_receiver, 212 notification_manager, 213 ); 214 local.spawn_local(async move { 215 task::spawn_local(async move { notification_driver.run().await }).await.unwrap(); 216 }); 217 notification_runtime.block_on(local); 218 }); 219 match driver_status_receiver.blocking_recv() { 220 Some(true) => Ok(()), 221 _ => Err(Error::Unknown), 222 } 223 } 224 225 /// Set logger mode. set_logger_mode(&self, logger_mode: UciLoggerMode) -> Result<()>226 pub fn set_logger_mode(&self, logger_mode: UciLoggerMode) -> Result<()> { 227 self.runtime_handle.block_on(self.uci_manager.set_logger_mode(logger_mode)) 228 } 229 /// Start UCI HAL and blocking until UCI commands can be sent. open_hal(&self) -> Result<GetDeviceInfoResponse>230 pub fn open_hal(&self) -> Result<GetDeviceInfoResponse> { 231 self.runtime_handle.block_on(self.uci_manager.open_hal()) 232 } 233 234 /// Stop the UCI HAL. close_hal(&self, force: bool) -> Result<()>235 pub fn close_hal(&self, force: bool) -> Result<()> { 236 self.runtime_handle.block_on(self.uci_manager.close_hal(force)) 237 } 238 239 // Methods for sending UCI commands. Functions are blocked until UCI response is received. 240 /// Send UCI command for device reset. device_reset(&self, reset_config: ResetConfig) -> Result<()>241 pub fn device_reset(&self, reset_config: ResetConfig) -> Result<()> { 242 self.runtime_handle.block_on(self.uci_manager.device_reset(reset_config)) 243 } 244 245 /// Send UCI command for getting device info. core_get_device_info(&self) -> Result<GetDeviceInfoResponse>246 pub fn core_get_device_info(&self) -> Result<GetDeviceInfoResponse> { 247 self.runtime_handle.block_on(self.uci_manager.core_get_device_info()) 248 } 249 250 /// Send UCI command for getting capability info core_get_caps_info(&self) -> Result<Vec<CapTlv>>251 pub fn core_get_caps_info(&self) -> Result<Vec<CapTlv>> { 252 self.runtime_handle.block_on(self.uci_manager.core_get_caps_info()) 253 } 254 255 /// Send UCI command for setting core configuration. core_set_config( &self, config_tlvs: Vec<DeviceConfigTlv>, ) -> Result<CoreSetConfigResponse>256 pub fn core_set_config( 257 &self, 258 config_tlvs: Vec<DeviceConfigTlv>, 259 ) -> Result<CoreSetConfigResponse> { 260 self.runtime_handle.block_on(self.uci_manager.core_set_config(config_tlvs)) 261 } 262 263 /// Send UCI command for getting core configuration. core_get_config(&self, config_ids: Vec<DeviceConfigId>) -> Result<Vec<DeviceConfigTlv>>264 pub fn core_get_config(&self, config_ids: Vec<DeviceConfigId>) -> Result<Vec<DeviceConfigTlv>> { 265 self.runtime_handle.block_on(self.uci_manager.core_get_config(config_ids)) 266 } 267 268 /// Send UCI command for getting uwbs timestamp. core_query_uwb_timestamp(&self) -> Result<u64>269 pub fn core_query_uwb_timestamp(&self) -> Result<u64> { 270 self.runtime_handle.block_on(self.uci_manager.core_query_uwb_timestamp()) 271 } 272 273 /// Send UCI command for initiating session. session_init(&self, session_id: SessionId, session_type: SessionType) -> Result<()>274 pub fn session_init(&self, session_id: SessionId, session_type: SessionType) -> Result<()> { 275 self.runtime_handle.block_on(self.uci_manager.session_init(session_id, session_type)) 276 } 277 278 /// Send UCI command for deinitiating session. session_deinit(&self, session_id: SessionId) -> Result<()>279 pub fn session_deinit(&self, session_id: SessionId) -> Result<()> { 280 self.runtime_handle.block_on(self.uci_manager.session_deinit(session_id)) 281 } 282 283 /// Send UCI command for setting app config. session_set_app_config( &self, session_id: SessionId, config_tlvs: Vec<AppConfigTlv>, ) -> Result<SetAppConfigResponse>284 pub fn session_set_app_config( 285 &self, 286 session_id: SessionId, 287 config_tlvs: Vec<AppConfigTlv>, 288 ) -> Result<SetAppConfigResponse> { 289 self.runtime_handle 290 .block_on(self.uci_manager.session_set_app_config(session_id, config_tlvs)) 291 } 292 293 /// Send UCI command for getting app config. session_get_app_config( &self, session_id: SessionId, config_ids: Vec<AppConfigTlvType>, ) -> Result<Vec<AppConfigTlv>>294 pub fn session_get_app_config( 295 &self, 296 session_id: SessionId, 297 config_ids: Vec<AppConfigTlvType>, 298 ) -> Result<Vec<AppConfigTlv>> { 299 self.runtime_handle 300 .block_on(self.uci_manager.session_get_app_config(session_id, config_ids)) 301 } 302 303 /// Send UCI command for getting count of sessions. session_get_count(&self) -> Result<u8>304 pub fn session_get_count(&self) -> Result<u8> { 305 self.runtime_handle.block_on(self.uci_manager.session_get_count()) 306 } 307 308 /// Send UCI command for getting state of session. session_get_state(&self, session_id: SessionId) -> Result<SessionState>309 pub fn session_get_state(&self, session_id: SessionId) -> Result<SessionState> { 310 self.runtime_handle.block_on(self.uci_manager.session_get_state(session_id)) 311 } 312 313 /// Send UCI command for updating multicast list for multicast session. session_update_controller_multicast_list( &self, session_id: SessionId, action: UpdateMulticastListAction, controlees: Controlees, is_multicast_list_ntf_v2_supported: bool, is_multicast_list_rsp_v2_supported: bool, ) -> Result<SessionUpdateControllerMulticastResponse>314 pub fn session_update_controller_multicast_list( 315 &self, 316 session_id: SessionId, 317 action: UpdateMulticastListAction, 318 controlees: Controlees, 319 is_multicast_list_ntf_v2_supported: bool, 320 is_multicast_list_rsp_v2_supported: bool, 321 ) -> Result<SessionUpdateControllerMulticastResponse> { 322 self.runtime_handle.block_on(self.uci_manager.session_update_controller_multicast_list( 323 session_id, 324 action, 325 controlees, 326 is_multicast_list_ntf_v2_supported, 327 is_multicast_list_rsp_v2_supported, 328 )) 329 } 330 331 /// Update ranging rounds for DT Tag session_update_dt_tag_ranging_rounds( &self, session_id: u32, ranging_round_indexes: Vec<u8>, ) -> Result<SessionUpdateDtTagRangingRoundsResponse>332 pub fn session_update_dt_tag_ranging_rounds( 333 &self, 334 session_id: u32, 335 ranging_round_indexes: Vec<u8>, 336 ) -> Result<SessionUpdateDtTagRangingRoundsResponse> { 337 self.runtime_handle.block_on( 338 self.uci_manager 339 .session_update_dt_tag_ranging_rounds(session_id, ranging_round_indexes), 340 ) 341 } 342 343 /// Send UCI command for getting max data size for session. session_query_max_data_size(&self, session_id: SessionId) -> Result<u16>344 pub fn session_query_max_data_size(&self, session_id: SessionId) -> Result<u16> { 345 self.runtime_handle.block_on(self.uci_manager.session_query_max_data_size(session_id)) 346 } 347 348 /// Send UCI command for starting ranging of the session. range_start(&self, session_id: SessionId) -> Result<()>349 pub fn range_start(&self, session_id: SessionId) -> Result<()> { 350 self.runtime_handle.block_on(self.uci_manager.range_start(session_id)) 351 } 352 353 /// Send UCI command for stopping ranging of the session. range_stop(&self, session_id: SessionId) -> Result<()>354 pub fn range_stop(&self, session_id: SessionId) -> Result<()> { 355 self.runtime_handle.block_on(self.uci_manager.range_stop(session_id)) 356 } 357 358 /// Send UCI command for getting ranging count. range_get_ranging_count(&self, session_id: SessionId) -> Result<usize>359 pub fn range_get_ranging_count(&self, session_id: SessionId) -> Result<usize> { 360 self.runtime_handle.block_on(self.uci_manager.range_get_ranging_count(session_id)) 361 } 362 363 /// Set the country code. Android-specific method. android_set_country_code(&self, country_code: CountryCode) -> Result<()>364 pub fn android_set_country_code(&self, country_code: CountryCode) -> Result<()> { 365 self.runtime_handle.block_on(self.uci_manager.android_set_country_code(country_code)) 366 } 367 368 /// Get the power statistics. Android-specific method. android_get_power_stats(&self) -> Result<PowerStats>369 pub fn android_get_power_stats(&self) -> Result<PowerStats> { 370 self.runtime_handle.block_on(self.uci_manager.android_get_power_stats()) 371 } 372 373 /// Set radar config. Android-specific method. android_set_radar_config( &self, session_id: SessionId, config_tlvs: Vec<RadarConfigTlv>, ) -> Result<AndroidRadarConfigResponse>374 pub fn android_set_radar_config( 375 &self, 376 session_id: SessionId, 377 config_tlvs: Vec<RadarConfigTlv>, 378 ) -> Result<AndroidRadarConfigResponse> { 379 self.runtime_handle 380 .block_on(self.uci_manager.android_set_radar_config(session_id, config_tlvs)) 381 } 382 383 /// Get radar config. Android-specific method. android_get_radar_config( &self, session_id: SessionId, config_ids: Vec<RadarConfigTlvType>, ) -> Result<Vec<RadarConfigTlv>>384 pub fn android_get_radar_config( 385 &self, 386 session_id: SessionId, 387 config_ids: Vec<RadarConfigTlvType>, 388 ) -> Result<Vec<RadarConfigTlv>> { 389 self.runtime_handle 390 .block_on(self.uci_manager.android_get_radar_config(session_id, config_ids)) 391 } 392 393 /// Send a raw UCI command. raw_uci_cmd( &self, mt: u32, gid: u32, oid: u32, payload: Vec<u8>, ) -> Result<RawUciMessage>394 pub fn raw_uci_cmd( 395 &self, 396 mt: u32, 397 gid: u32, 398 oid: u32, 399 payload: Vec<u8>, 400 ) -> Result<RawUciMessage> { 401 self.runtime_handle.block_on(self.uci_manager.raw_uci_cmd(mt, gid, oid, payload)) 402 } 403 404 /// Send a data packet send_data_packet( &self, session_id: SessionId, address: Vec<u8>, uci_sequence_num: u16, app_payload_data: Vec<u8>, ) -> Result<()>405 pub fn send_data_packet( 406 &self, 407 session_id: SessionId, 408 address: Vec<u8>, 409 uci_sequence_num: u16, 410 app_payload_data: Vec<u8>, 411 ) -> Result<()> { 412 self.runtime_handle.block_on(self.uci_manager.send_data_packet( 413 session_id, 414 address, 415 uci_sequence_num, 416 app_payload_data, 417 )) 418 } 419 420 /// Get session token for session id. get_session_token(&self, session_id: SessionId) -> Result<u32>421 pub fn get_session_token(&self, session_id: SessionId) -> Result<u32> { 422 self.runtime_handle.block_on(self.uci_manager.get_session_token_from_session_id(session_id)) 423 } 424 425 /// Send UCI command for setting hybrid controller configuration session_set_hybrid_controller_config( &self, session_id: SessionId, message_control: u8, number_of_phases: u8, update_time: UpdateTime, phase_list: PhaseList, ) -> Result<()>426 pub fn session_set_hybrid_controller_config( 427 &self, 428 session_id: SessionId, 429 message_control: u8, 430 number_of_phases: u8, 431 update_time: UpdateTime, 432 phase_list: PhaseList, 433 ) -> Result<()> { 434 self.runtime_handle.block_on(self.uci_manager.session_set_hybrid_controller_config( 435 session_id, 436 message_control, 437 number_of_phases, 438 update_time, 439 phase_list, 440 )) 441 } 442 443 /// Send UCI command for setting hybrid controlee configuration session_set_hybrid_controlee_config( &self, session_id: SessionId, controlee_phase_list: Vec<ControleePhaseList>, ) -> Result<()>444 pub fn session_set_hybrid_controlee_config( 445 &self, 446 session_id: SessionId, 447 controlee_phase_list: Vec<ControleePhaseList>, 448 ) -> Result<()> { 449 self.runtime_handle.block_on( 450 self.uci_manager.session_set_hybrid_controlee_config(session_id, controlee_phase_list), 451 ) 452 } 453 454 /// Send UCI command for session data transfer phase config session_data_transfer_phase_config( &self, session_id: SessionId, dtpcm_repetition: u8, data_transfer_control: u8, dtpml_size: u8, mac_address: Vec<u8>, slot_bitmap: Vec<u8>, ) -> Result<()>455 pub fn session_data_transfer_phase_config( 456 &self, 457 session_id: SessionId, 458 dtpcm_repetition: u8, 459 data_transfer_control: u8, 460 dtpml_size: u8, 461 mac_address: Vec<u8>, 462 slot_bitmap: Vec<u8>, 463 ) -> Result<()> { 464 self.runtime_handle.block_on(self.uci_manager.session_data_transfer_phase_config( 465 session_id, 466 dtpcm_repetition, 467 data_transfer_control, 468 dtpml_size, 469 mac_address, 470 slot_bitmap, 471 )) 472 } 473 } 474 475 impl UciManagerSync<UciManagerImpl> { 476 /// Constructor. 477 /// 478 /// UciHal and NotificationManagerBuilder required at construction as they are required before 479 /// open_hal is called. runtime_handle must be a Handle to a multithread runtime that outlives 480 /// UciManagerSyncImpl. 481 /// 482 /// Implementation note: An explicit decision is made to not use UciManagerImpl as a parameter. 483 /// UciManagerImpl::new() appears to be sync, but needs an async context to be called, but the 484 /// user is unlikely to be aware of this technicality. new<H, B, L>( hal: H, notification_manager_builder: B, logger: L, logger_mode: UciLoggerMode, runtime_handle: Handle, ) -> Result<Self> where H: UciHal, B: NotificationManagerBuilder, L: UciLogger,485 pub fn new<H, B, L>( 486 hal: H, 487 notification_manager_builder: B, 488 logger: L, 489 logger_mode: UciLoggerMode, 490 runtime_handle: Handle, 491 ) -> Result<Self> 492 where 493 H: UciHal, 494 B: NotificationManagerBuilder, 495 L: UciLogger, 496 { 497 // UciManagerImpl::new uses tokio::spawn, so it is called inside the runtime as async fn. 498 let uci_manager = 499 runtime_handle.block_on(async { UciManagerImpl::new(hal, logger, logger_mode) }); 500 let mut uci_manager_sync = UciManagerSync { runtime_handle, uci_manager }; 501 uci_manager_sync.redirect_notification(notification_manager_builder)?; 502 Ok(uci_manager_sync) 503 } 504 } 505 506 #[cfg(any(test, feature = "mock-utils"))] 507 impl UciManagerSync<MockUciManager> { 508 /// Constructor for mock version. new_mock<T: NotificationManagerBuilder>( uci_manager: MockUciManager, runtime_handle: Handle, notification_manager_builder: T, ) -> Result<Self>509 pub fn new_mock<T: NotificationManagerBuilder>( 510 uci_manager: MockUciManager, 511 runtime_handle: Handle, 512 notification_manager_builder: T, 513 ) -> Result<Self> { 514 let mut uci_manager_sync = UciManagerSync { uci_manager, runtime_handle }; 515 uci_manager_sync.redirect_notification(notification_manager_builder)?; 516 Ok(uci_manager_sync) 517 } 518 } 519 520 #[cfg(test)] 521 mod tests { 522 use super::*; 523 524 use std::cell::RefCell; 525 use std::rc::Rc; 526 527 use tokio::runtime::Builder; 528 use uwb_uci_packets::DeviceState::DeviceStateReady; 529 530 use crate::params::uci_packets::GetDeviceInfoResponse; 531 use crate::uci::mock_uci_manager::MockUciManager; 532 use crate::uci::{CoreNotification, UciNotification}; 533 use uwb_uci_packets::StatusCode::UciStatusOk; 534 535 /// Mock NotificationManager forwarding notifications received. 536 /// The nonsend_counter is deliberately !send to check UciManagerSync::redirect_notification. 537 struct MockNotificationManager { 538 notf_sender: mpsc::UnboundedSender<UciNotification>, 539 // nonsend_counter is an example of a !Send property. 540 nonsend_counter: Rc<RefCell<usize>>, 541 } 542 543 impl NotificationManager for MockNotificationManager { on_core_notification(&mut self, core_notification: CoreNotification) -> Result<()>544 fn on_core_notification(&mut self, core_notification: CoreNotification) -> Result<()> { 545 self.nonsend_counter.replace_with(|&mut prev| prev + 1); 546 self.notf_sender 547 .send(UciNotification::Core(core_notification)) 548 .map_err(|_| Error::Unknown) 549 } on_session_notification( &mut self, session_notification: SessionNotification, ) -> Result<()>550 fn on_session_notification( 551 &mut self, 552 session_notification: SessionNotification, 553 ) -> Result<()> { 554 self.nonsend_counter.replace_with(|&mut prev| prev + 1); 555 self.notf_sender 556 .send(UciNotification::Session(session_notification)) 557 .map_err(|_| Error::Unknown) 558 } on_vendor_notification(&mut self, vendor_notification: RawUciMessage) -> Result<()>559 fn on_vendor_notification(&mut self, vendor_notification: RawUciMessage) -> Result<()> { 560 self.nonsend_counter.replace_with(|&mut prev| prev + 1); 561 self.notf_sender 562 .send(UciNotification::Vendor(vendor_notification)) 563 .map_err(|_| Error::Unknown) 564 } on_data_rcv_notification(&mut self, _data_rcv_notf: DataRcvNotification) -> Result<()>565 fn on_data_rcv_notification(&mut self, _data_rcv_notf: DataRcvNotification) -> Result<()> { 566 self.nonsend_counter.replace_with(|&mut prev| prev + 1); 567 Ok(()) 568 } on_radar_data_rcv_notification( &mut self, _data_rcv_notf: RadarDataRcvNotification, ) -> Result<()>569 fn on_radar_data_rcv_notification( 570 &mut self, 571 _data_rcv_notf: RadarDataRcvNotification, 572 ) -> Result<()> { 573 self.nonsend_counter.replace_with(|&mut prev| prev + 1); 574 Ok(()) 575 } 576 } 577 578 /// Builder for MockNotificationManager. 579 struct MockNotificationManagerBuilder { 580 notf_sender: mpsc::UnboundedSender<UciNotification>, 581 // initial_count is an example for a parameter undetermined at compile time. 582 } 583 584 impl MockNotificationManagerBuilder { 585 /// Constructor for builder. new(notf_sender: mpsc::UnboundedSender<UciNotification>) -> Self586 fn new(notf_sender: mpsc::UnboundedSender<UciNotification>) -> Self { 587 Self { notf_sender } 588 } 589 } 590 591 impl NotificationManagerBuilder for MockNotificationManagerBuilder { 592 type NotificationManager = MockNotificationManager; 593 build(self) -> Option<Self::NotificationManager>594 fn build(self) -> Option<Self::NotificationManager> { 595 Some(MockNotificationManager { 596 notf_sender: self.notf_sender, 597 nonsend_counter: Rc::new(RefCell::new(0)), 598 }) 599 } 600 } 601 602 #[test] 603 /// Tests that the Command, Response, and Notification pipeline are functional. test_sync_uci_basic_sequence()604 fn test_sync_uci_basic_sequence() { 605 let test_rt = Builder::new_multi_thread().enable_all().build().unwrap(); 606 let (notf_sender, mut notf_receiver) = mpsc::unbounded_channel::<UciNotification>(); 607 let mut uci_manager_impl = MockUciManager::new(); 608 let get_device_info_rsp = GetDeviceInfoResponse { 609 status: UciStatusOk, 610 uci_version: 0, 611 mac_version: 0, 612 phy_version: 0, 613 uci_test_version: 0, 614 vendor_spec_info: vec![], 615 }; 616 617 uci_manager_impl.expect_open_hal( 618 vec![UciNotification::Core(CoreNotification::DeviceStatus(DeviceStateReady))], 619 Ok(get_device_info_rsp.clone()), 620 ); 621 uci_manager_impl.expect_core_get_device_info(Ok(get_device_info_rsp)); 622 let uci_manager_sync = UciManagerSync::new_mock( 623 uci_manager_impl, 624 test_rt.handle().to_owned(), 625 MockNotificationManagerBuilder::new(notf_sender), 626 ) 627 .unwrap(); 628 assert!(uci_manager_sync.open_hal().is_ok()); 629 let device_state = test_rt.block_on(async { notf_receiver.recv().await }); 630 assert!(device_state.is_some()); 631 assert!(uci_manager_sync.core_get_device_info().is_ok()); 632 } 633 } 634