• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 pub mod uci_hmsgs;
18 pub mod uci_hrcv;
19 pub mod uci_logger;
20 
21 use crate::adaptation::{UwbAdaptation, UwbAdaptationImpl};
22 use crate::error::UwbErr;
23 use crate::event_manager::EventManager;
24 use crate::uci::uci_hrcv::UciResponse;
25 use android_hardware_uwb::aidl::android::hardware::uwb::{
26     UwbEvent::UwbEvent, UwbStatus::UwbStatus,
27 };
28 use arbitrary::Arbitrary;
29 use log::{debug, error, info, warn};
30 use std::future::Future;
31 use std::option::Option;
32 use std::sync::Arc;
33 use tokio::runtime::{Builder, Runtime};
34 use tokio::sync::{mpsc, oneshot, Notify};
35 use tokio::{select, task};
36 use uwb_uci_packets::{
37     AndroidGetPowerStatsCmdBuilder, DeviceState, DeviceStatusNtfBuilder, GetCapsInfoCmdBuilder,
38     GetDeviceInfoCmdBuilder, GetDeviceInfoRspPacket, RangeStartCmdBuilder, RangeStopCmdBuilder,
39     SessionDeinitCmdBuilder, SessionGetAppConfigCmdBuilder, SessionGetCountCmdBuilder,
40     SessionGetStateCmdBuilder, SessionState, SessionStatusNtfPacket, StatusCode, UciCommandPacket,
41 };
42 
43 pub type Result<T> = std::result::Result<T, UwbErr>;
44 pub type UciResponseHandle = oneshot::Sender<UciResponse>;
45 pub type SyncUwbAdaptation = Arc<dyn UwbAdaptation + Send + Sync>;
46 
47 // Commands sent from JNI.
48 #[derive(Arbitrary, Clone, Debug, PartialEq, Eq)]
49 pub enum JNICommand {
50     // Blocking UCI commands
51     UciGetCapsInfo,
52     UciGetDeviceInfo,
53     UciSessionInit(u32, u8),
54     UciSessionDeinit(u32),
55     UciSessionGetCount,
56     UciStartRange(u32),
57     UciStopRange(u32),
58     UciGetSessionState(u32),
59     UciSessionUpdateMulticastList {
60         session_id: u32,
61         action: u8,
62         no_of_controlee: u8,
63         address_list: Vec<i16>,
64         sub_session_id_list: Vec<i32>,
65     },
66     UciSetCountryCode {
67         code: Vec<u8>,
68     },
69     UciSetAppConfig {
70         session_id: u32,
71         no_of_params: u32,
72         // TODO this field should be removed, in tandem with a change to the Uwb APEX
73         app_config_param_len: u32,
74         app_configs: Vec<u8>,
75     },
76     UciGetAppConfig {
77         session_id: u32,
78         no_of_params: u32,
79         app_config_param_len: u32,
80         app_configs: Vec<u8>,
81     },
82     UciRawVendorCmd {
83         gid: u32,
84         oid: u32,
85         payload: Vec<u8>,
86     },
87     UciDeviceReset {
88         reset_config: u8,
89     },
90     UciGetPowerStats,
91 
92     // Non blocking commands
93     Enable,
94     Disable(bool),
95 }
96 
97 // Responses from the HAL.
98 #[derive(Debug)]
99 pub enum HalCallback {
100     Event { event: UwbEvent, event_status: UwbStatus },
101     UciRsp(uci_hrcv::UciResponse),
102     UciNtf(uci_hrcv::UciNotification),
103 }
104 
105 #[derive(Debug, PartialEq)]
106 pub enum UwbState {
107     None,
108     W4HalOpen,
109     Ready,
110     W4UciResp,
111     W4HalClose,
112 }
113 
114 #[derive(Clone)]
115 struct Retryer {
116     received: Arc<Notify>,
117     failed: Arc<Notify>,
118     retry: Arc<Notify>,
119 }
120 
121 impl Retryer {
new() -> Self122     fn new() -> Self {
123         Self {
124             received: Arc::new(Notify::new()),
125             failed: Arc::new(Notify::new()),
126             retry: Arc::new(Notify::new()),
127         }
128     }
129 
command_failed(&self)130     async fn command_failed(&self) {
131         self.failed.notified().await
132     }
133 
immediate_retry(&self)134     async fn immediate_retry(&self) {
135         self.retry.notified().await
136     }
137 
command_serviced(&self)138     async fn command_serviced(&self) {
139         self.received.notified().await
140     }
141 
received(&self)142     fn received(&self) {
143         self.received.notify_one()
144     }
145 
retry(&self)146     fn retry(&self) {
147         self.retry.notify_one()
148     }
149 
failed(&self)150     fn failed(&self) {
151         self.failed.notify_one()
152     }
153 
send_with_retry(self, adaptation: SyncUwbAdaptation, cmd: UciCommandPacket)154     fn send_with_retry(self, adaptation: SyncUwbAdaptation, cmd: UciCommandPacket) {
155         tokio::task::spawn(async move {
156             let mut received_response = false;
157             for retry in 0..MAX_RETRIES {
158                 adaptation.send_uci_message(cmd.clone()).await.unwrap_or_else(|e| {
159                     error!("Sending UCI message failed: {:?}", e);
160                 });
161                 select! {
162                     _ = tokio::time::sleep(tokio::time::Duration::from_millis(RETRY_DELAY_MS)) => warn!("UWB chip did not respond within {}ms deadline. Retrying (#{})...", RETRY_DELAY_MS, retry + 1),
163                     _ = self.command_serviced() => {
164                         received_response = true;
165                         break;
166                     }
167                     _ = self.immediate_retry() => debug!("UWB chip requested immediate retry. Retrying (#{})...", retry + 1),
168                 }
169             }
170             if !received_response {
171                 error!("After {} retries, no response from UWB chip", MAX_RETRIES);
172                 adaptation.core_initialization().await.unwrap_or_else(|e| {
173                     error!("Resetting chip due to no responses failed: {:?}", e);
174                 });
175                 self.failed();
176             }
177         });
178     }
179 }
180 
181 //TODO pull in libfutures instead of open-coding this
option_future<R, T: Future<Output = R>>(mf: Option<T>) -> Option<R>182 async fn option_future<R, T: Future<Output = R>>(mf: Option<T>) -> Option<R> {
183     if let Some(f) = mf {
184         Some(f.await)
185     } else {
186         None
187     }
188 }
189 
190 struct Driver<T: EventManager> {
191     adaptation: SyncUwbAdaptation,
192     event_manager: T,
193     cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>,
194     rsp_receiver: mpsc::UnboundedReceiver<HalCallback>,
195     response_channel: Option<(UciResponseHandle, Retryer)>,
196     state: UwbState,
197 }
198 
199 // Creates a future that handles messages from JNI and the HAL.
drive<T: EventManager + Send + Sync>( adaptation: SyncUwbAdaptation, event_manager: T, cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>, rsp_receiver: mpsc::UnboundedReceiver<HalCallback>, ) -> Result<()>200 async fn drive<T: EventManager + Send + Sync>(
201     adaptation: SyncUwbAdaptation,
202     event_manager: T,
203     cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>,
204     rsp_receiver: mpsc::UnboundedReceiver<HalCallback>,
205 ) -> Result<()> {
206     Driver::new(adaptation, event_manager, cmd_receiver, rsp_receiver).await.drive().await
207 }
208 
209 const MAX_RETRIES: usize = 5;
210 const RETRY_DELAY_MS: u64 = 800;
211 
212 impl<T: EventManager> Driver<T> {
new( adaptation: SyncUwbAdaptation, event_manager: T, cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>, rsp_receiver: mpsc::UnboundedReceiver<HalCallback>, ) -> Self213     async fn new(
214         adaptation: SyncUwbAdaptation,
215         event_manager: T,
216         cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>,
217         rsp_receiver: mpsc::UnboundedReceiver<HalCallback>,
218     ) -> Self {
219         Self {
220             adaptation,
221             event_manager,
222             cmd_receiver,
223             rsp_receiver,
224             response_channel: None,
225             state: UwbState::None,
226         }
227     }
228 
229     // Continually handles messages.
drive(mut self) -> Result<()>230     async fn drive(mut self) -> Result<()> {
231         loop {
232             match self.drive_once().await {
233                 Err(UwbErr::Exit) => return Ok(()),
234                 Err(e) => error!("drive_once: {:?}", e),
235                 Ok(()) => (),
236             }
237         }
238     }
239 
handle_blocking_jni_cmd( &mut self, tx: oneshot::Sender<UciResponse>, cmd: JNICommand, ) -> Result<()>240     async fn handle_blocking_jni_cmd(
241         &mut self,
242         tx: oneshot::Sender<UciResponse>,
243         cmd: JNICommand,
244     ) -> Result<()> {
245         log::debug!("Received blocking cmd {:?}", cmd);
246         let command: UciCommandPacket = match cmd {
247             JNICommand::UciGetDeviceInfo => GetDeviceInfoCmdBuilder {}.build().into(),
248             JNICommand::UciGetCapsInfo => GetCapsInfoCmdBuilder {}.build().into(),
249             JNICommand::UciSessionInit(session_id, session_type) => {
250                 uci_hmsgs::build_session_init_cmd(session_id, session_type)?.build().into()
251             }
252             JNICommand::UciSessionDeinit(session_id) => {
253                 SessionDeinitCmdBuilder { session_id }.build().into()
254             }
255             JNICommand::UciSessionGetCount => SessionGetCountCmdBuilder {}.build().into(),
256             JNICommand::UciGetPowerStats => AndroidGetPowerStatsCmdBuilder {}.build().into(),
257             JNICommand::UciStartRange(session_id) => {
258                 RangeStartCmdBuilder { session_id }.build().into()
259             }
260             JNICommand::UciStopRange(session_id) => {
261                 RangeStopCmdBuilder { session_id }.build().into()
262             }
263             JNICommand::UciGetSessionState(session_id) => {
264                 SessionGetStateCmdBuilder { session_id }.build().into()
265             }
266             JNICommand::UciSessionUpdateMulticastList {
267                 session_id,
268                 action,
269                 no_of_controlee,
270                 ref address_list,
271                 ref sub_session_id_list,
272             } => uci_hmsgs::build_multicast_list_update_cmd(
273                 session_id,
274                 action,
275                 no_of_controlee,
276                 address_list,
277                 sub_session_id_list,
278             )?
279             .build()
280             .into(),
281             JNICommand::UciSetCountryCode { ref code } => {
282                 uci_hmsgs::build_set_country_code_cmd(code)?.build().into()
283             }
284             JNICommand::UciSetAppConfig { session_id, no_of_params, ref app_configs, .. } => {
285                 uci_hmsgs::build_set_app_config_cmd(session_id, no_of_params, app_configs)?
286                     .build()
287                     .into()
288             }
289             JNICommand::UciGetAppConfig { session_id, ref app_configs, .. } => {
290                 SessionGetAppConfigCmdBuilder { session_id, app_cfg: app_configs.to_vec() }
291                     .build()
292                     .into()
293             }
294             JNICommand::UciRawVendorCmd { gid, oid, payload } => {
295                 uci_hmsgs::build_uci_vendor_cmd_packet(gid, oid, payload)?
296             }
297             JNICommand::UciDeviceReset { reset_config } => {
298                 uci_hmsgs::build_device_reset_cmd(reset_config)?.build().into()
299             }
300             _ => {
301                 error!("Unexpected blocking cmd received {:?}", cmd);
302                 return Ok(());
303             }
304         };
305 
306         log::debug!("Sending HAL UCI message {:?}", command);
307 
308         let retryer = Retryer::new();
309         self.response_channel = Some((tx, retryer.clone()));
310         retryer.send_with_retry(self.adaptation.clone(), command);
311         self.set_state(UwbState::W4UciResp);
312         Ok(())
313     }
314 
handle_non_blocking_jni_cmd(&mut self, cmd: JNICommand) -> Result<()>315     async fn handle_non_blocking_jni_cmd(&mut self, cmd: JNICommand) -> Result<()> {
316         log::debug!("Received non blocking cmd {:?}", cmd);
317         match cmd {
318             JNICommand::Enable => {
319                 self.adaptation.hal_open().await?;
320                 self.adaptation.core_initialization().await?;
321                 self.set_state(UwbState::W4HalOpen);
322             }
323             JNICommand::Disable(_graceful) => {
324                 self.adaptation.hal_close().await?;
325                 self.set_state(UwbState::W4HalClose);
326             }
327             _ => {
328                 error!("Unexpected non blocking cmd received {:?}", cmd);
329                 return Ok(());
330             }
331         }
332         Ok(())
333     }
334 
handle_hal_notification(&self, response: uci_hrcv::UciNotification) -> Result<()>335     async fn handle_hal_notification(&self, response: uci_hrcv::UciNotification) -> Result<()> {
336         log::debug!("Received hal notification {:?}", response);
337         match response {
338             uci_hrcv::UciNotification::DeviceStatusNtf(response) => {
339                 self.event_manager.device_status_notification_received(response)?;
340             }
341             uci_hrcv::UciNotification::GenericError(response) => {
342                 if let (StatusCode::UciStatusCommandRetry, Some((_, retryer))) =
343                     (response.get_status(), self.response_channel.as_ref())
344                 {
345                     retryer.retry();
346                 }
347                 self.event_manager.core_generic_error_notification_received(response)?;
348             }
349             uci_hrcv::UciNotification::SessionStatusNtf(response) => {
350                 self.invoke_hal_session_init_if_necessary(&response).await;
351                 self.event_manager.session_status_notification_received(response)?;
352             }
353             uci_hrcv::UciNotification::ShortMacTwoWayRangeDataNtf(response) => {
354                 self.event_manager.short_range_data_notification_received(response)?;
355             }
356             uci_hrcv::UciNotification::ExtendedMacTwoWayRangeDataNtf(response) => {
357                 self.event_manager.extended_range_data_notification_received(response)?;
358             }
359             uci_hrcv::UciNotification::SessionUpdateControllerMulticastListNtf(response) => {
360                 self.event_manager
361                     .session_update_controller_multicast_list_notification_received(response)?;
362             }
363             uci_hrcv::UciNotification::RawVendorNtf(response) => {
364                 self.event_manager.vendor_uci_notification_received(response)?;
365             }
366         }
367         Ok(())
368     }
369 
370     // Handles a single message from JNI or the HAL.
drive_once(&mut self) -> Result<()>371     async fn drive_once(&mut self) -> Result<()> {
372         select! {
373             Some(()) = option_future(self.response_channel.as_ref()
374                 .map(|(_, retryer)| retryer.command_failed())) => {
375                 // TODO: Do we want to flush the incoming queue of commands when this happens?
376                 self.set_state(UwbState::W4HalOpen);
377                 self.response_channel = None
378             }
379             // Note: If a blocking command is awaiting a response, any non-blocking commands are not
380             // dequeued until the blocking cmd's response is received.
381             Some((cmd, tx)) = self.cmd_receiver.recv(), if self.can_process_cmd() => {
382                 match tx {
383                     Some(tx) => { // Blocking JNI commands processing.
384                         self.handle_blocking_jni_cmd(tx, cmd).await?;
385                     },
386                     None => { // Non Blocking JNI commands processing.
387                         self.handle_non_blocking_jni_cmd(cmd).await?;
388                     }
389                 }
390             }
391             Some(rsp) = self.rsp_receiver.recv() => {
392                 match rsp {
393                     HalCallback::Event{event, event_status} => {
394                         log::info!("Received HAL event: {:?} with status: {:?}", event, event_status);
395                         match event {
396                             UwbEvent::POST_INIT_CPLT => {
397                                 self.set_state(UwbState::Ready);
398                             }
399                             UwbEvent::CLOSE_CPLT => {
400                                 self.set_state(UwbState::None);
401                                 return Err(UwbErr::Exit);
402                             }
403                             UwbEvent::ERROR => {
404                                 // Send device status notification with error state.
405                                 let device_status_ntf = DeviceStatusNtfBuilder { device_state: DeviceState::DeviceStateError}.build();
406                                 self.event_manager.device_status_notification_received(device_status_ntf)?;
407                                 self.set_state(UwbState::None);
408                             }
409                             _ => ()
410                         }
411                     },
412                     HalCallback::UciRsp(response) => {
413                         log::debug!("Received HAL UCI message {:?}", response);
414                         self.set_state(UwbState::Ready);
415                         if let Some((channel, retryer)) = self.response_channel.take() {
416                             retryer.received();
417                             channel.send(response).unwrap_or_else(|_| {
418                                 error!("Unable to send response, receiver gone");
419                             });
420                         } else {
421                             error!("Received response packet, but no response channel available");
422                         }
423                     },
424                     HalCallback::UciNtf(response) => {
425                         self.handle_hal_notification(response).await?;
426                     }
427                 }
428             }
429         }
430         Ok(())
431     }
432 
433     // Triggers the session init HAL API, if a new session is initialized.
invoke_hal_session_init_if_necessary(&self, response: &SessionStatusNtfPacket)434     async fn invoke_hal_session_init_if_necessary(&self, response: &SessionStatusNtfPacket) {
435         if let SessionState::SessionStateInit = response.get_session_state() {
436             info!(
437                 "Session {:?} initialized, invoking session init HAL API",
438                 response.get_session_id()
439             );
440             self.adaptation
441                 // HAL API accepts signed int, so cast received session_id as i32.
442                 .session_initialization(response.get_session_id() as i32)
443                 .await
444                 .unwrap_or_else(|e| error!("Error invoking session init HAL API : {:?}", e));
445         }
446     }
447 
set_state(&mut self, state: UwbState)448     fn set_state(&mut self, state: UwbState) {
449         info!("UWB state change from {:?} to {:?}", self.state, state);
450         self.state = state;
451     }
452 
can_process_cmd(&mut self) -> bool453     fn can_process_cmd(&mut self) -> bool {
454         self.state == UwbState::None || self.state == UwbState::Ready
455     }
456 }
457 
458 pub trait Dispatcher {
send_jni_command(&self, cmd: JNICommand) -> Result<()>459     fn send_jni_command(&self, cmd: JNICommand) -> Result<()>;
block_on_jni_command(&self, cmd: JNICommand) -> Result<UciResponse>460     fn block_on_jni_command(&self, cmd: JNICommand) -> Result<UciResponse>;
wait_for_exit(&mut self) -> Result<()>461     fn wait_for_exit(&mut self) -> Result<()>;
get_device_info(&self) -> &Option<GetDeviceInfoRspPacket>462     fn get_device_info(&self) -> &Option<GetDeviceInfoRspPacket>;
set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>)463     fn set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>);
464 }
465 
466 // Controller for sending tasks for the native thread to handle.
467 pub struct DispatcherImpl {
468     cmd_sender: mpsc::UnboundedSender<(JNICommand, Option<UciResponseHandle>)>,
469     join_handle: task::JoinHandle<Result<()>>,
470     runtime: Runtime,
471     device_info: Option<GetDeviceInfoRspPacket>,
472 }
473 
474 impl DispatcherImpl {
new<T: 'static + EventManager + Send + Sync>(event_manager: T) -> Result<Self>475     pub fn new<T: 'static + EventManager + Send + Sync>(event_manager: T) -> Result<Self> {
476         let (rsp_sender, rsp_receiver) = mpsc::unbounded_channel::<HalCallback>();
477         // TODO when simplifying constructors, avoid spare runtime
478         let adaptation: SyncUwbAdaptation = Arc::new(
479             Builder::new_current_thread().build()?.block_on(UwbAdaptationImpl::new(rsp_sender))?,
480         );
481 
482         Self::new_with_args(event_manager, adaptation, rsp_receiver)
483     }
484 
new_for_testing<T: 'static + EventManager + Send + Sync>( event_manager: T, adaptation: SyncUwbAdaptation, rsp_receiver: mpsc::UnboundedReceiver<HalCallback>, ) -> Result<Self>485     pub fn new_for_testing<T: 'static + EventManager + Send + Sync>(
486         event_manager: T,
487         adaptation: SyncUwbAdaptation,
488         rsp_receiver: mpsc::UnboundedReceiver<HalCallback>,
489     ) -> Result<Self> {
490         Self::new_with_args(event_manager, adaptation, rsp_receiver)
491     }
492 
new_with_args<T: 'static + EventManager + Send + Sync>( event_manager: T, adaptation: SyncUwbAdaptation, rsp_receiver: mpsc::UnboundedReceiver<HalCallback>, ) -> Result<Self>493     fn new_with_args<T: 'static + EventManager + Send + Sync>(
494         event_manager: T,
495         adaptation: SyncUwbAdaptation,
496         rsp_receiver: mpsc::UnboundedReceiver<HalCallback>,
497     ) -> Result<Self> {
498         info!("initializing dispatcher");
499         let (cmd_sender, cmd_receiver) =
500             mpsc::unbounded_channel::<(JNICommand, Option<UciResponseHandle>)>();
501 
502         // We create a new thread here both to avoid reusing the Java service thread and because
503         // binder threads will call into this.
504         let runtime = Builder::new_multi_thread()
505             .worker_threads(1)
506             .thread_name("uwb-uci-handler")
507             .enable_all()
508             .build()?;
509         let join_handle =
510             runtime.spawn(drive(adaptation, event_manager, cmd_receiver, rsp_receiver));
511         Ok(DispatcherImpl { cmd_sender, join_handle, runtime, device_info: None })
512     }
513 }
514 
515 impl Dispatcher for DispatcherImpl {
send_jni_command(&self, cmd: JNICommand) -> Result<()>516     fn send_jni_command(&self, cmd: JNICommand) -> Result<()> {
517         self.cmd_sender.send((cmd, None))?;
518         Ok(())
519     }
520 
521     // TODO: Consider implementing these separate for different commands so we can have more
522     // specific return types.
block_on_jni_command(&self, cmd: JNICommand) -> Result<UciResponse>523     fn block_on_jni_command(&self, cmd: JNICommand) -> Result<UciResponse> {
524         let (tx, rx) = oneshot::channel();
525         self.cmd_sender.send((cmd, Some(tx)))?;
526         let ret = self.runtime.block_on(rx)?;
527         log::trace!("{:?}", ret);
528         Ok(ret)
529     }
530 
wait_for_exit(&mut self) -> Result<()>531     fn wait_for_exit(&mut self) -> Result<()> {
532         match self.runtime.block_on(&mut self.join_handle) {
533             Err(err) if err.is_panic() => {
534                 error!("Driver thread is panic!");
535                 Err(UwbErr::Undefined)
536             }
537             _ => Ok(()),
538         }
539     }
540 
get_device_info(&self) -> &Option<GetDeviceInfoRspPacket>541     fn get_device_info(&self) -> &Option<GetDeviceInfoRspPacket> {
542         &self.device_info
543     }
set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>)544     fn set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>) {
545         self.device_info = device_info;
546     }
547 }
548 
549 #[cfg(test)]
550 pub mod mock_uci_logger;
551 
552 #[cfg(test)]
553 mod tests {
554     use self::uci_hrcv::UciNotification;
555     use self::uci_hrcv::UciResponse;
556 
557     use super::*;
558     use crate::adaptation::mock_adaptation::MockUwbAdaptation;
559     use crate::event_manager::mock_event_manager::MockEventManager;
560     use android_hardware_uwb::aidl::android::hardware::uwb::{
561         UwbEvent::UwbEvent, UwbStatus::UwbStatus,
562     };
563     use num_traits::ToPrimitive;
564     use uwb_uci_packets::*;
565 
setup_dispatcher_and_return_hal_cb_sender( config_fn: fn(&mut Arc<MockUwbAdaptation>, &mut MockEventManager), ) -> (DispatcherImpl, mpsc::UnboundedSender<HalCallback>)566     fn setup_dispatcher_and_return_hal_cb_sender(
567         config_fn: fn(&mut Arc<MockUwbAdaptation>, &mut MockEventManager),
568     ) -> (DispatcherImpl, mpsc::UnboundedSender<HalCallback>) {
569         // TODO: Remove this once we call it somewhere real.
570         logger::init(
571             logger::Config::default().with_tag_on_device("uwb").with_min_level(log::Level::Debug),
572         );
573 
574         let (rsp_sender, rsp_receiver) = mpsc::unbounded_channel::<HalCallback>();
575         let mut mock_adaptation = Arc::new(MockUwbAdaptation::new(rsp_sender.clone()));
576         let mut mock_event_manager = MockEventManager::new();
577 
578         config_fn(&mut mock_adaptation, &mut mock_event_manager);
579         let dispatcher = DispatcherImpl::new_for_testing(
580             mock_event_manager,
581             mock_adaptation as SyncUwbAdaptation,
582             rsp_receiver,
583         )
584         .unwrap();
585         (dispatcher, rsp_sender)
586     }
587 
generate_fake_get_device_cmd_rsp() -> (GetDeviceInfoCmdPacket, GetDeviceInfoRspPacket)588     fn generate_fake_get_device_cmd_rsp() -> (GetDeviceInfoCmdPacket, GetDeviceInfoRspPacket) {
589         let cmd = GetDeviceInfoCmdBuilder {}.build();
590         let rsp = GetDeviceInfoRspBuilder {
591             status: StatusCode::UciStatusOk,
592             uci_version: 0,
593             mac_version: 0,
594             phy_version: 0,
595             uci_test_version: 0,
596             vendor_spec_info: vec![],
597         }
598         .build();
599         (cmd, rsp)
600     }
601 
generate_fake_device_status_ntf() -> DeviceStatusNtfPacket602     fn generate_fake_device_status_ntf() -> DeviceStatusNtfPacket {
603         DeviceStatusNtfBuilder { device_state: DeviceState::DeviceStateReady }.build()
604     }
605 
606     #[test]
test_initialize()607     fn test_initialize() {
608         let (dispatcher, _hal_sender) =
609             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
610                 mock_adaptation.expect_hal_open(Ok(()));
611                 mock_adaptation.expect_core_initialization(Ok(()));
612             });
613 
614         dispatcher.send_jni_command(JNICommand::Enable).unwrap();
615     }
616 
617     #[test]
test_hal_error_event()618     fn test_hal_error_event() {
619         let (dispatcher, hal_sender) =
620             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, mock_event_manager| {
621                 mock_adaptation.expect_hal_open(Ok(()));
622                 mock_adaptation.expect_core_initialization(Ok(()));
623                 mock_event_manager.expect_device_status_notification_received(Ok(()));
624             });
625 
626         dispatcher.send_jni_command(JNICommand::Enable).unwrap();
627         hal_sender
628             .send(HalCallback::Event { event: UwbEvent::ERROR, event_status: UwbStatus::FAILED })
629             .unwrap();
630     }
631 
632     #[test]
test_deinitialize()633     fn test_deinitialize() {
634         let (mut dispatcher, hal_sender) =
635             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
636                 mock_adaptation.expect_hal_close(Ok(()));
637             });
638 
639         dispatcher.send_jni_command(JNICommand::Disable(true)).unwrap();
640         hal_sender
641             .send(HalCallback::Event { event: UwbEvent::CLOSE_CPLT, event_status: UwbStatus::OK })
642             .unwrap();
643         dispatcher.wait_for_exit().unwrap();
644     }
645 
646     #[test]
test_get_device_info()647     fn test_get_device_info() {
648         let (dispatcher, _hal_sender) =
649             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
650                 let (cmd, rsp) = generate_fake_get_device_cmd_rsp();
651                 mock_adaptation.expect_send_uci_message(
652                     cmd.into(),
653                     Some(UciResponse::GetDeviceInfoRsp(rsp)),
654                     None,
655                     Ok(()),
656                 );
657             });
658 
659         dispatcher.block_on_jni_command(JNICommand::UciGetDeviceInfo).unwrap();
660     }
661 
662     #[test]
test_get_device_info_with_uci_retry()663     fn test_get_device_info_with_uci_retry() {
664         let (dispatcher, _hal_sender) =
665             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
666                 let (cmd, rsp) = generate_fake_get_device_cmd_rsp();
667 
668                 // Let the first 2 tries not response data, then the 3rd tries response successfully.
669                 mock_adaptation.expect_send_uci_message(cmd.clone().into(), None, None, Ok(()));
670                 mock_adaptation.expect_send_uci_message(cmd.clone().into(), None, None, Ok(()));
671                 mock_adaptation.expect_send_uci_message(
672                     cmd.into(),
673                     Some(UciResponse::GetDeviceInfoRsp(rsp)),
674                     None,
675                     Ok(()),
676                 );
677             });
678 
679         dispatcher.block_on_jni_command(JNICommand::UciGetDeviceInfo).unwrap();
680     }
681 
682     #[test]
test_get_device_info_send_uci_message_failed()683     fn test_get_device_info_send_uci_message_failed() {
684         let (dispatcher, _hal_sender) =
685             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
686                 let (cmd, _rsp) = generate_fake_get_device_cmd_rsp();
687                 mock_adaptation.expect_send_uci_message(
688                     cmd.into(),
689                     None,
690                     None,
691                     Err(UwbErr::failed()),
692                 );
693             });
694 
695         dispatcher
696             .block_on_jni_command(JNICommand::UciGetDeviceInfo)
697             .expect_err("This method should fail.");
698     }
699 
700     #[test]
test_device_status_notification()701     fn test_device_status_notification() {
702         let (dispatcher, _hal_sender) =
703             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, mock_event_manager| {
704                 let (cmd, rsp) = generate_fake_get_device_cmd_rsp();
705                 let ntf = generate_fake_device_status_ntf();
706                 mock_adaptation.expect_send_uci_message(
707                     cmd.into(),
708                     Some(UciResponse::GetDeviceInfoRsp(rsp)),
709                     Some(UciNotification::DeviceStatusNtf(ntf)),
710                     Ok(()),
711                 );
712                 mock_event_manager.expect_device_status_notification_received(Ok(()));
713             });
714 
715         dispatcher.block_on_jni_command(JNICommand::UciGetDeviceInfo).unwrap();
716     }
717 
718     #[test]
test_get_caps_info()719     fn test_get_caps_info() {
720         let (dispatcher, _hal_sender) =
721             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
722                 let cmd = GetCapsInfoCmdBuilder {}.build();
723                 let rsp =
724                     GetCapsInfoRspBuilder { status: StatusCode::UciStatusOk, tlvs: Vec::new() }
725                         .build();
726                 mock_adaptation.expect_send_uci_message(
727                     cmd.into(),
728                     Some(UciResponse::GetCapsInfoRsp(rsp)),
729                     None,
730                     Ok(()),
731                 );
732             });
733 
734         dispatcher.block_on_jni_command(JNICommand::UciGetCapsInfo).unwrap();
735     }
736 
737     #[test]
test_session_init()738     fn test_session_init() {
739         let (dispatcher, _hal_sender) =
740             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
741                 let cmd = SessionInitCmdBuilder {
742                     session_id: 1,
743                     session_type: SessionType::FiraRangingSession,
744                 }
745                 .build();
746                 let rsp = SessionInitRspBuilder { status: StatusCode::UciStatusOk }.build();
747                 mock_adaptation.expect_send_uci_message(
748                     cmd.into(),
749                     Some(UciResponse::SessionInitRsp(rsp)),
750                     None,
751                     Ok(()),
752                 );
753             });
754 
755         dispatcher
756             .block_on_jni_command(JNICommand::UciSessionInit(
757                 1,
758                 SessionType::FiraRangingSession.to_u8().unwrap(),
759             ))
760             .unwrap();
761     }
762 
763     #[test]
test_session_deinit()764     fn test_session_deinit() {
765         let (dispatcher, _hal_sender) =
766             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
767                 let cmd = SessionDeinitCmdBuilder { session_id: 1 }.build();
768                 let rsp = SessionDeinitRspBuilder { status: StatusCode::UciStatusOk }.build();
769                 mock_adaptation.expect_send_uci_message(
770                     cmd.into(),
771                     Some(UciResponse::SessionDeinitRsp(rsp)),
772                     None,
773                     Ok(()),
774                 );
775             });
776 
777         dispatcher.block_on_jni_command(JNICommand::UciSessionDeinit(1)).unwrap();
778     }
779 
780     #[test]
test_session_get_count()781     fn test_session_get_count() {
782         let (dispatcher, _hal_sender) =
783             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
784                 let cmd = SessionGetCountCmdBuilder {}.build();
785                 let rsp =
786                     SessionGetCountRspBuilder { status: StatusCode::UciStatusOk, session_count: 5 }
787                         .build();
788                 mock_adaptation.expect_send_uci_message(
789                     cmd.into(),
790                     Some(UciResponse::SessionGetCountRsp(rsp)),
791                     None,
792                     Ok(()),
793                 );
794             });
795 
796         dispatcher.block_on_jni_command(JNICommand::UciSessionGetCount).unwrap();
797     }
798 
799     #[test]
test_start_range()800     fn test_start_range() {
801         let (dispatcher, _hal_sender) =
802             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
803                 let cmd = RangeStartCmdBuilder { session_id: 5 }.build();
804                 let rsp = RangeStartRspBuilder { status: StatusCode::UciStatusOk }.build();
805                 mock_adaptation.expect_send_uci_message(
806                     cmd.into(),
807                     Some(UciResponse::RangeStartRsp(rsp)),
808                     None,
809                     Ok(()),
810                 );
811             });
812 
813         dispatcher.block_on_jni_command(JNICommand::UciStartRange(5)).unwrap();
814     }
815 
816     #[test]
test_stop_range()817     fn test_stop_range() {
818         let (dispatcher, _hal_sender) =
819             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
820                 let cmd = RangeStopCmdBuilder { session_id: 5 }.build();
821                 let rsp = RangeStopRspBuilder { status: StatusCode::UciStatusOk }.build();
822                 mock_adaptation.expect_send_uci_message(
823                     cmd.into(),
824                     Some(UciResponse::RangeStopRsp(rsp)),
825                     None,
826                     Ok(()),
827                 );
828             });
829 
830         dispatcher.block_on_jni_command(JNICommand::UciStopRange(5)).unwrap();
831     }
832 
833     #[test]
test_get_session_state()834     fn test_get_session_state() {
835         let (dispatcher, _hal_sender) =
836             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
837                 let cmd = SessionGetStateCmdBuilder { session_id: 5 }.build();
838                 let rsp = SessionGetStateRspBuilder {
839                     status: StatusCode::UciStatusOk,
840                     session_state: SessionState::SessionStateActive,
841                 }
842                 .build();
843                 mock_adaptation.expect_send_uci_message(
844                     cmd.into(),
845                     Some(UciResponse::SessionGetStateRsp(rsp)),
846                     None,
847                     Ok(()),
848                 );
849             });
850 
851         dispatcher.block_on_jni_command(JNICommand::UciGetSessionState(5)).unwrap();
852     }
853 
854     #[test]
test_session_update_multicast_list()855     fn test_session_update_multicast_list() {
856         let (dispatcher, _hal_sender) =
857             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
858                 let cmd = SessionUpdateControllerMulticastListCmdBuilder {
859                     session_id: 5,
860                     action: UpdateMulticastListAction::AddControlee,
861                     controlees: Vec::new(),
862                 }
863                 .build();
864                 let rsp = SessionUpdateControllerMulticastListRspBuilder {
865                     status: StatusCode::UciStatusOk,
866                 }
867                 .build();
868                 mock_adaptation.expect_send_uci_message(
869                     cmd.into(),
870                     Some(UciResponse::SessionUpdateControllerMulticastListRsp(rsp)),
871                     None,
872                     Ok(()),
873                 );
874             });
875 
876         dispatcher
877             .block_on_jni_command(JNICommand::UciSessionUpdateMulticastList {
878                 session_id: 5,
879                 action: UpdateMulticastListAction::AddControlee.to_u8().unwrap(),
880                 no_of_controlee: 0,
881                 address_list: Vec::new(),
882                 sub_session_id_list: Vec::new(),
883             })
884             .unwrap();
885     }
886 
887     #[test]
test_set_country_code()888     fn test_set_country_code() {
889         let (dispatcher, _hal_sender) =
890             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
891                 let cmd = AndroidSetCountryCodeCmdBuilder { country_code: [45, 34] }.build();
892                 let rsp =
893                     AndroidSetCountryCodeRspBuilder { status: StatusCode::UciStatusOk }.build();
894                 mock_adaptation.expect_send_uci_message(
895                     cmd.into(),
896                     Some(UciResponse::AndroidSetCountryCodeRsp(rsp)),
897                     None,
898                     Ok(()),
899                 );
900             });
901 
902         dispatcher
903             .block_on_jni_command(JNICommand::UciSetCountryCode { code: vec![45, 34] })
904             .unwrap();
905     }
906 
907     #[test]
test_set_app_config()908     fn test_set_app_config() {
909         let (dispatcher, _hal_sender) =
910             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
911                 let cmd = SessionSetAppConfigCmdBuilder { session_id: 5, tlvs: Vec::new() }.build();
912                 let rsp = SessionSetAppConfigRspBuilder {
913                     status: StatusCode::UciStatusOk,
914                     cfg_status: Vec::new(),
915                 }
916                 .build();
917                 mock_adaptation.expect_send_uci_message(
918                     cmd.into(),
919                     Some(UciResponse::SessionSetAppConfigRsp(rsp)),
920                     None,
921                     Ok(()),
922                 );
923             });
924 
925         dispatcher
926             .block_on_jni_command(JNICommand::UciSetAppConfig {
927                 session_id: 5,
928                 no_of_params: 0,
929                 app_config_param_len: 0,
930                 app_configs: Vec::new(),
931             })
932             .unwrap();
933     }
934 
935     #[test]
test_get_app_config()936     fn test_get_app_config() {
937         let (dispatcher, _hal_sender) =
938             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
939                 let cmd =
940                     SessionGetAppConfigCmdBuilder { session_id: 5, app_cfg: Vec::new() }.build();
941                 let rsp = SessionGetAppConfigRspBuilder {
942                     status: StatusCode::UciStatusOk,
943                     tlvs: Vec::new(),
944                 }
945                 .build();
946                 mock_adaptation.expect_send_uci_message(
947                     cmd.into(),
948                     Some(UciResponse::SessionGetAppConfigRsp(rsp)),
949                     None,
950                     Ok(()),
951                 );
952             });
953 
954         dispatcher
955             .block_on_jni_command(JNICommand::UciGetAppConfig {
956                 session_id: 5,
957                 no_of_params: 0,
958                 app_config_param_len: 0,
959                 app_configs: Vec::new(),
960             })
961             .unwrap();
962     }
963 
964     #[test]
test_raw_vendor_cmd()965     fn test_raw_vendor_cmd() {
966         let (dispatcher, _hal_sender) =
967             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
968                 let cmd = UciVendor_9_CommandBuilder { opcode: 5, payload: None }.build();
969                 let rsp = UciVendor_9_ResponseBuilder { opcode: 5, payload: None }.build();
970                 mock_adaptation.expect_send_uci_message(
971                     cmd.into(),
972                     Some(UciResponse::RawVendorRsp(rsp.into())),
973                     None,
974                     Ok(()),
975                 );
976             });
977 
978         dispatcher
979             .block_on_jni_command(JNICommand::UciRawVendorCmd {
980                 gid: 9,
981                 oid: 5,
982                 payload: Vec::new(),
983             })
984             .unwrap();
985     }
986 
987     #[test]
test_device_reset()988     fn test_device_reset() {
989         let (dispatcher, _hal_sender) =
990             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
991                 let cmd = DeviceResetCmdBuilder { reset_config: ResetConfig::UwbsReset }.build();
992                 let rsp = DeviceResetRspBuilder { status: StatusCode::UciStatusOk }.build();
993                 mock_adaptation.expect_send_uci_message(
994                     cmd.into(),
995                     Some(UciResponse::DeviceResetRsp(rsp)),
996                     None,
997                     Ok(()),
998                 );
999             });
1000 
1001         dispatcher.block_on_jni_command(JNICommand::UciDeviceReset { reset_config: 0 }).unwrap();
1002     }
1003 
1004     #[test]
test_get_power_stats()1005     fn test_get_power_stats() {
1006         let (dispatcher, _hal_sender) =
1007             setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| {
1008                 let cmd = AndroidGetPowerStatsCmdBuilder {}.build();
1009                 let rsp = AndroidGetPowerStatsRspBuilder {
1010                     stats: PowerStats {
1011                         status: StatusCode::UciStatusOk,
1012                         idle_time_ms: 0,
1013                         tx_time_ms: 0,
1014                         rx_time_ms: 0,
1015                         total_wake_count: 0,
1016                     },
1017                 }
1018                 .build();
1019                 mock_adaptation.expect_send_uci_message(
1020                     cmd.into(),
1021                     Some(UciResponse::AndroidGetPowerStatsRsp(rsp)),
1022                     None,
1023                     Ok(()),
1024                 );
1025             });
1026 
1027         dispatcher.block_on_jni_command(JNICommand::UciGetPowerStats).unwrap();
1028     }
1029 }
1030