• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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