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