diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2022-02-26 04:22:17 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2022-02-26 04:22:17 +0000 |
commit | c3aab1e047396f54d95cd76ca09ccba797e5bac2 (patch) | |
tree | 0e7091ae381c7e3207adc55eb0bea6e8f64ae567 /src | |
parent | bf494c749999349deb9ad3b0d5cf3a96f9658a7d (diff) | |
parent | 290fcf2943e1d065c056ca0199a14f1d902813ad (diff) | |
download | uwb-c3aab1e047396f54d95cd76ca09ccba797e5bac2.tar.gz |
Merge "uwb(uci-rust): Uci Logging" into tm-dev
Diffstat (limited to 'src')
-rw-r--r-- | src/rust/adaptation/mod.rs | 498 | ||||
-rw-r--r-- | src/rust/uci/mod.rs | 21 | ||||
-rw-r--r-- | src/rust/uci/uci_hrcv.rs | 4 | ||||
-rw-r--r-- | src/rust/uci/uci_logger.rs | 292 |
4 files changed, 566 insertions, 249 deletions
diff --git a/src/rust/adaptation/mod.rs b/src/rust/adaptation/mod.rs index f12870d..2dfbcc1 100644 --- a/src/rust/adaptation/mod.rs +++ b/src/rust/adaptation/mod.rs @@ -2,6 +2,7 @@ use crate::error::UwbErr; use crate::uci::uci_hrcv; +use crate::uci::uci_logger::{UciLogMode, UciLogger, UciLoggerImpl}; use crate::uci::HalCallback; use android_hardware_uwb::aidl::android::hardware::uwb::{ IUwb::IUwbAsync, @@ -14,19 +15,30 @@ use android_hardware_uwb::binder::{BinderFeatures, Interface, Result as BinderRe use async_trait::async_trait; use binder_tokio::{Tokio, TokioRuntime}; use log::error; +use std::sync::Arc; use tokio::runtime::Handle; use tokio::sync::mpsc; -use uwb_uci_packets::UciPacketPacket; +use uwb_uci_packets::{Packet, UciCommandPacket, UciPacketChild, UciPacketPacket}; type Result<T> = std::result::Result<T, UwbErr>; +type SyncUciLogger = Arc<dyn UciLogger + Send + Sync>; pub struct UwbClientCallback { rsp_sender: mpsc::UnboundedSender<HalCallback>, + logger: SyncUciLogger, } impl UwbClientCallback { - fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Self { - UwbClientCallback { rsp_sender } + fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>, logger: SyncUciLogger) -> Self { + UwbClientCallback { rsp_sender, logger } + } + + async fn log_uci_packet(&self, packet: UciPacketPacket) { + match packet.specialize() { + UciPacketChild::UciResponse(pkt) => self.logger.log_uci_response(pkt).await, + UciPacketChild::UciNotification(pkt) => self.logger.log_uci_notification(pkt).await, + _ => {} + } } } @@ -43,19 +55,18 @@ impl IUwbClientCallbackAsyncServer for UwbClientCallback { async fn onUciMessage(&self, data: &[u8]) -> BinderResult<()> { match UciPacketPacket::parse(data) { - Ok(evt) => { - let packet_msg = uci_hrcv::uci_message(evt); + Ok(packet) => { + self.log_uci_packet(packet.clone()).await; + let packet_msg = uci_hrcv::uci_message(packet); match packet_msg { Ok(uci_hrcv::UciMessage::Response(evt)) => self .rsp_sender .send(HalCallback::UciRsp(evt)) .unwrap_or_else(|e| error!("Error sending uci response: {:?}", e)), - Ok(uci_hrcv::UciMessage::Notification(evt)) => self .rsp_sender .send(HalCallback::UciNtf(evt)) .unwrap_or_else(|e| error!("Error sending uci notification: {:?}", e)), - _ => error!("UCI message which is neither a UCI RSP or NTF: {:?}", data), } } @@ -80,19 +91,21 @@ pub trait UwbAdaptation { async fn hal_close(&self) -> Result<()>; async fn core_initialization(&self) -> Result<()>; async fn session_initialization(&self, session_id: i32) -> Result<()>; - async fn send_uci_message(&self, data: &[u8]) -> Result<()>; + async fn send_uci_message(&self, cmd: UciCommandPacket) -> Result<()>; } #[derive(Clone)] pub struct UwbAdaptationImpl { hal: Strong<dyn IUwbChipAsync<Tokio>>, rsp_sender: mpsc::UnboundedSender<HalCallback>, + logger: SyncUciLogger, } impl UwbAdaptationImpl { pub async fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Result<Self> { let hal = get_hal_service().await?; - Ok(UwbAdaptationImpl { hal, rsp_sender }) + let logger = UciLoggerImpl::new(UciLogMode::Filtered).await; + Ok(UwbAdaptationImpl { hal, rsp_sender, logger: Arc::new(logger) }) } } @@ -102,7 +115,7 @@ impl UwbAdaptation for UwbAdaptationImpl { async fn hal_open(&self) -> Result<()> { let m_cback = BnUwbClientCallback::new_async_binder( - UwbClientCallback::new(self.rsp_sender.clone()), + UwbClientCallback::new(self.rsp_sender.clone(), self.logger.clone()), TokioRuntime(Handle::current()), BinderFeatures::default(), ); @@ -110,6 +123,7 @@ impl UwbAdaptation for UwbAdaptationImpl { } async fn hal_close(&self) -> Result<()> { + self.logger.close_file().await; Ok(self.hal.close().await?) } @@ -121,287 +135,297 @@ impl UwbAdaptation for UwbAdaptationImpl { Ok(self.hal.sessionInit(session_id).await?) } - async fn send_uci_message(&self, data: &[u8]) -> Result<()> { - self.hal.sendUciMessage(data).await?; + async fn send_uci_message(&self, cmd: UciCommandPacket) -> Result<()> { + self.logger.log_uci_command(cmd.clone()).await; + self.hal.sendUciMessage(&cmd.to_vec()).await?; // TODO should we be validating the returned number? Ok(()) } } #[cfg(test)] -use log::warn; -#[cfg(test)] -use std::collections::VecDeque; -#[cfg(test)] -use std::sync::Mutex; - -#[cfg(test)] -enum ExpectedCall { - Finalize { - expected_exit_status: bool, - }, - HalOpen { - out: Result<()>, - }, - HalClose { - out: Result<()>, - }, - CoreInitialization { - out: Result<()>, - }, - SessionInitialization { - expected_session_id: i32, - out: Result<()>, - }, - SendUciMessage { - expected_data: Vec<u8>, - rsp_data: Option<Vec<u8>>, - notf_data: Option<Vec<u8>>, - out: Result<()>, - }, -} - -#[cfg(test)] -pub struct MockUwbAdaptation { - rsp_sender: mpsc::UnboundedSender<HalCallback>, - expected_calls: Mutex<VecDeque<ExpectedCall>>, -} - -#[cfg(test)] -impl MockUwbAdaptation { - pub fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Self { - Self { rsp_sender, expected_calls: Mutex::new(VecDeque::new()) } +pub mod tests { + #![allow(non_snake_case)] + use super::*; + use crate::uci::uci_logger::MockUciLogger; + use log::warn; + use std::collections::VecDeque; + use std::sync::Mutex as StdMutex; + + enum ExpectedCall { + Finalize { + expected_exit_status: bool, + }, + HalOpen { + out: Result<()>, + }, + HalClose { + out: Result<()>, + }, + CoreInitialization { + out: Result<()>, + }, + SessionInitialization { + expected_session_id: i32, + out: Result<()>, + }, + SendUciMessage { + expected_data: Vec<u8>, + rsp_data: Option<Vec<u8>>, + notf_data: Option<Vec<u8>>, + out: Result<()>, + }, } - #[allow(dead_code)] - pub fn expect_finalize(&mut self, expected_exit_status: bool) { - self.expected_calls - .lock() - .unwrap() - .push_back(ExpectedCall::Finalize { expected_exit_status }); - } - #[allow(dead_code)] - pub fn expect_hal_open(&mut self, out: Result<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::HalOpen { out }); - } - #[allow(dead_code)] - pub fn expect_hal_close(&mut self, out: Result<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::HalClose { out }); - } - #[allow(dead_code)] - pub fn expect_core_initialization(&mut self, out: Result<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::CoreInitialization { out }); - } - #[allow(dead_code)] - pub fn expect_session_initialization(&mut self, expected_session_id: i32, out: Result<()>) { - self.expected_calls - .lock() - .unwrap() - .push_back(ExpectedCall::SessionInitialization { expected_session_id, out }); - } - #[allow(dead_code)] - pub fn expect_send_uci_message( - &mut self, - expected_data: Vec<u8>, - rsp_data: Option<Vec<u8>>, - notf_data: Option<Vec<u8>>, - out: Result<()>, - ) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::SendUciMessage { - expected_data, - rsp_data, - notf_data, - out, - }); + pub struct MockUwbAdaptation { + rsp_sender: mpsc::UnboundedSender<HalCallback>, + expected_calls: StdMutex<VecDeque<ExpectedCall>>, } - async fn send_client_event(&self, event: UwbEvent, status: UwbStatus) { - let uwb_client_callback = UwbClientCallback::new(self.rsp_sender.clone()); - let _ = uwb_client_callback.onHalEvent(event, status).await; - } + impl MockUwbAdaptation { + pub fn new(rsp_sender: mpsc::UnboundedSender<HalCallback>) -> Self { + Self { rsp_sender, expected_calls: StdMutex::new(VecDeque::new()) } + } - async fn send_client_message(&self, rsp_data: Vec<u8>) { - let uwb_client_callback = UwbClientCallback::new(self.rsp_sender.clone()); - let _ = uwb_client_callback.onUciMessage(&rsp_data).await; - } -} + #[allow(dead_code)] + pub fn expect_finalize(&mut self, expected_exit_status: bool) { + self.expected_calls + .lock() + .unwrap() + .push_back(ExpectedCall::Finalize { expected_exit_status }); + } + #[allow(dead_code)] + pub fn expect_hal_open(&mut self, out: Result<()>) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::HalOpen { out }); + } + #[allow(dead_code)] + pub fn expect_hal_close(&mut self, out: Result<()>) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::HalClose { out }); + } + #[allow(dead_code)] + pub fn expect_core_initialization(&mut self, out: Result<()>) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::CoreInitialization { out }); + } + #[allow(dead_code)] + pub fn expect_session_initialization(&mut self, expected_session_id: i32, out: Result<()>) { + self.expected_calls + .lock() + .unwrap() + .push_back(ExpectedCall::SessionInitialization { expected_session_id, out }); + } + #[allow(dead_code)] + pub fn expect_send_uci_message( + &mut self, + expected_data: Vec<u8>, + rsp_data: Option<Vec<u8>>, + notf_data: Option<Vec<u8>>, + out: Result<()>, + ) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::SendUciMessage { + expected_data, + rsp_data, + notf_data, + out, + }); + } -#[cfg(test)] -impl Drop for MockUwbAdaptation { - fn drop(&mut self) { - assert!(self.expected_calls.lock().unwrap().is_empty()); + fn create_uwb_client_callback( + rsp_sender: mpsc::UnboundedSender<HalCallback>, + ) -> UwbClientCallback { + // Add tests for the mock logger. + UwbClientCallback::new(rsp_sender, Arc::new(MockUciLogger::new())) + } + + async fn send_client_event(&self, event: UwbEvent, status: UwbStatus) { + let uwb_client_callback = + MockUwbAdaptation::create_uwb_client_callback(self.rsp_sender.clone()); + let _ = uwb_client_callback.onHalEvent(event, status).await; + } + + async fn send_client_message(&self, rsp_data: Vec<u8>) { + let uwb_client_callback = + MockUwbAdaptation::create_uwb_client_callback(self.rsp_sender.clone()); + let _ = uwb_client_callback.onUciMessage(&rsp_data).await; + } } -} -#[cfg(test)] -#[async_trait] -impl UwbAdaptation for MockUwbAdaptation { - async fn finalize(&mut self, exit_status: bool) { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::Finalize { expected_exit_status }) - if expected_exit_status == exit_status => - { - return; - } - Some(call) => { - expected_calls.push_front(call); - } - None => {} + impl Drop for MockUwbAdaptation { + fn drop(&mut self) { + assert!(self.expected_calls.lock().unwrap().is_empty()); } - warn!("unpected finalize() called"); } - async fn hal_open(&self) -> Result<()> { - let expected_out = { + #[async_trait] + impl UwbAdaptation for MockUwbAdaptation { + async fn finalize(&mut self, exit_status: bool) { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { - Some(ExpectedCall::HalOpen { out }) => Some(out), + Some(ExpectedCall::Finalize { expected_exit_status }) + if expected_exit_status == exit_status => + { + return; + } Some(call) => { expected_calls.push_front(call); - None } - None => None, - } - }; - - match expected_out { - Some(out) => { - let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; - self.send_client_event(UwbEvent::OPEN_CPLT, status).await; - out - } - None => { - warn!("unpected hal_open() called"); - Err(UwbErr::Undefined) + None => {} } + warn!("unpected finalize() called"); } - } - async fn hal_close(&self) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::HalClose { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None + async fn hal_open(&self) -> Result<()> { + let expected_out = { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::HalOpen { out }) => Some(out), + Some(call) => { + expected_calls.push_front(call); + None + } + None => None, } - None => None, - } - }; + }; - match expected_out { - Some(out) => { - let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; - self.send_client_event(UwbEvent::CLOSE_CPLT, status).await; - out - } - None => { - warn!("unpected hal_close() called"); - Err(UwbErr::Undefined) + match expected_out { + Some(out) => { + let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; + self.send_client_event(UwbEvent::OPEN_CPLT, status).await; + out + } + None => { + warn!("unpected hal_open() called"); + Err(UwbErr::Undefined) + } } } - } - async fn core_initialization(&self) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::CoreInitialization { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None + async fn hal_close(&self) -> Result<()> { + let expected_out = { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::HalClose { out }) => Some(out), + Some(call) => { + expected_calls.push_front(call); + None + } + None => None, } - None => None, - } - }; + }; - match expected_out { - Some(out) => { - let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; - self.send_client_event(UwbEvent::POST_INIT_CPLT, status).await; - out - } - None => { - warn!("unpected core_initialization() called"); - Err(UwbErr::Undefined) + match expected_out { + Some(out) => { + let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; + self.send_client_event(UwbEvent::CLOSE_CPLT, status).await; + out + } + None => { + warn!("unpected hal_close() called"); + Err(UwbErr::Undefined) + } } } - } - async fn session_initialization(&self, session_id: i32) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::SessionInitialization { expected_session_id, out }) - if expected_session_id == session_id => - { - Some(out) - } - Some(call) => { - expected_calls.push_front(call); - None + async fn core_initialization(&self) -> Result<()> { + let expected_out = { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::CoreInitialization { out }) => Some(out), + Some(call) => { + expected_calls.push_front(call); + None + } + None => None, } - None => None, - } - }; + }; - match expected_out { - Some(out) => out, - None => { - warn!("unpected session_initialization() called"); - Err(UwbErr::Undefined) + match expected_out { + Some(out) => { + let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; + self.send_client_event(UwbEvent::POST_INIT_CPLT, status).await; + out + } + None => { + warn!("unpected core_initialization() called"); + Err(UwbErr::Undefined) + } } } - } - async fn send_uci_message(&self, data: &[u8]) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::SendUciMessage { expected_data, rsp_data, notf_data, out }) - if expected_data == data => - { - Some((rsp_data, notf_data, out)) + async fn session_initialization(&self, session_id: i32) -> Result<()> { + let expected_out = { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::SessionInitialization { expected_session_id, out }) + if expected_session_id == session_id => + { + Some(out) + } + Some(call) => { + expected_calls.push_front(call); + None + } + None => None, } - Some(call) => { - expected_calls.push_front(call); - None + }; + + match expected_out { + Some(out) => out, + None => { + warn!("unpected session_initialization() called"); + Err(UwbErr::Undefined) } - None => None, } - }; + } - match expected_out { - Some((rsp_data, notf_data, out)) => { - if let Some(rsp) = rsp_data { - self.send_client_message(rsp).await; + async fn send_uci_message(&self, cmd: UciCommandPacket) -> Result<()> { + let expected_out = { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::SendUciMessage { + expected_data, + rsp_data, + notf_data, + out, + }) if expected_data == cmd.to_vec() => Some((rsp_data, notf_data, out)), + Some(call) => { + expected_calls.push_front(call); + None + } + None => None, } - if let Some(notf) = notf_data { - self.send_client_message(notf).await; + }; + + match expected_out { + Some((rsp_data, notf_data, out)) => { + if let Some(rsp) = rsp_data { + self.send_client_message(rsp).await; + } + if let Some(notf) = notf_data { + self.send_client_message(notf).await; + } + out + } + None => { + warn!("unpected send_uci_message() called"); + Err(UwbErr::Undefined) } - out - } - None => { - warn!("unpected send_uci_message() called"); - Err(UwbErr::Undefined) } } } -} -#[cfg(test)] -mod tests { - #![allow(non_snake_case)] - use super::*; + fn create_uwb_client_callback( + rsp_sender: mpsc::UnboundedSender<HalCallback>, + ) -> UwbClientCallback { + // Add tests for the mock logger. + UwbClientCallback::new(rsp_sender, Arc::new(MockUciLogger::new())) + } #[tokio::test] async fn test_onHalEvent() { let uwb_event_test = UwbEvent(0); let uwb_status_test = UwbStatus(1); let (rsp_sender, _) = mpsc::unbounded_channel::<HalCallback>(); - let uwb_client_callback_test = UwbClientCallback::new(rsp_sender); + let uwb_client_callback_test = create_uwb_client_callback(rsp_sender); let result = uwb_client_callback_test.onHalEvent(uwb_event_test, uwb_status_test).await; assert_eq!(result, Ok(())); } @@ -413,7 +437,7 @@ mod tests { 0x0a, ]; let (rsp_sender, mut rsp_receiver) = mpsc::unbounded_channel::<HalCallback>(); - let uwb_client_callback_test = UwbClientCallback::new(rsp_sender); + let uwb_client_callback_test = create_uwb_client_callback(rsp_sender); let result = uwb_client_callback_test.onUciMessage(&data).await; assert_eq!(result, Ok(())); let response = rsp_receiver.recv().await; @@ -430,7 +454,7 @@ mod tests { 0x0a, ]; let (rsp_sender, mut rsp_receiver) = mpsc::unbounded_channel::<HalCallback>(); - let uwb_client_callback_test = UwbClientCallback::new(rsp_sender); + let uwb_client_callback_test = create_uwb_client_callback(rsp_sender); let result = uwb_client_callback_test.onUciMessage(&data).await; assert_eq!(result, Ok(())); let response = rsp_receiver.try_recv(); diff --git a/src/rust/uci/mod.rs b/src/rust/uci/mod.rs index 6c0cad1..44acf11 100644 --- a/src/rust/uci/mod.rs +++ b/src/rust/uci/mod.rs @@ -16,6 +16,7 @@ pub mod uci_hmsgs; pub mod uci_hrcv; +pub mod uci_logger; use crate::adaptation::{UwbAdaptation, UwbAdaptationImpl}; use crate::error::UwbErr; @@ -33,9 +34,9 @@ use tokio::sync::{mpsc, oneshot, Notify}; use tokio::{select, task}; use uwb_uci_packets::{ AndroidGetPowerStatsCmdBuilder, GetCapsInfoCmdBuilder, GetDeviceInfoCmdBuilder, - GetDeviceInfoRspPacket, Packet, RangeStartCmdBuilder, RangeStopCmdBuilder, - SessionDeinitCmdBuilder, SessionGetAppConfigCmdBuilder, SessionGetCountCmdBuilder, - SessionGetStateCmdBuilder, SessionState, SessionStatusNtfPacket, StatusCode, UciCommandPacket, + GetDeviceInfoRspPacket, RangeStartCmdBuilder, RangeStopCmdBuilder, SessionDeinitCmdBuilder, + SessionGetAppConfigCmdBuilder, SessionGetCountCmdBuilder, SessionGetStateCmdBuilder, + SessionState, SessionStatusNtfPacket, StatusCode, UciCommandPacket, }; pub type Result<T> = std::result::Result<T, UwbErr>; @@ -150,11 +151,11 @@ impl Retryer { self.failed.notify_one() } - fn send_with_retry(self, adaptation: Arc<SyncUwbAdaptation>, bytes: Vec<u8>) { + fn send_with_retry(self, adaptation: Arc<SyncUwbAdaptation>, cmd: UciCommandPacket) { tokio::task::spawn(async move { let mut received_response = false; for retry in 0..MAX_RETRIES { - adaptation.send_uci_message(&bytes).await.unwrap_or_else(|e| { + adaptation.send_uci_message(cmd.clone()).await.unwrap_or_else(|e| { error!("Sending UCI message failed: {:?}", e); }); select! { @@ -202,14 +203,14 @@ async fn drive<T: EventManager + Send + Sync>( cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>, rsp_receiver: mpsc::UnboundedReceiver<HalCallback>, ) -> Result<()> { - Driver::new(Arc::new(adaptation), event_manager, cmd_receiver, rsp_receiver).drive().await + Driver::new(Arc::new(adaptation), event_manager, cmd_receiver, rsp_receiver).await.drive().await } const MAX_RETRIES: usize = 5; const RETRY_DELAY_MS: u64 = 800; impl<T: EventManager> Driver<T> { - fn new( + async fn new( adaptation: Arc<SyncUwbAdaptation>, event_manager: T, cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>)>, @@ -314,7 +315,7 @@ impl<T: EventManager> Driver<T> { let retryer = Retryer::new(); self.response_channel = Some((tx, retryer.clone())); - retryer.send_with_retry(self.adaptation.clone(), command.to_vec()); + retryer.send_with_retry(self.adaptation.clone(), command); self.set_state(UwbState::W4UciResp); Ok(()) } @@ -533,9 +534,9 @@ impl Dispatcher { #[cfg(test)] mod tests { use super::*; - use crate::adaptation::MockUwbAdaptation; + use crate::adaptation::tests::MockUwbAdaptation; use crate::event_manager::MockEventManager; - use uwb_uci_packets::{DeviceState, DeviceStatusNtfBuilder, GetDeviceInfoRspBuilder}; + use uwb_uci_packets::{DeviceState, DeviceStatusNtfBuilder, GetDeviceInfoRspBuilder, Packet}; fn setup_dispatcher( config_fn: fn(&mut Box<MockUwbAdaptation>, &mut MockEventManager), diff --git a/src/rust/uci/uci_hrcv.rs b/src/rust/uci/uci_hrcv.rs index 8a8dae6..bf6562b 100644 --- a/src/rust/uci/uci_hrcv.rs +++ b/src/rust/uci/uci_hrcv.rs @@ -23,7 +23,7 @@ pub enum UciMessage { Notification(UciNotification), } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum UciResponse { GetDeviceInfoRsp(GetDeviceInfoRspPacket), GetCapsInfoRsp(GetCapsInfoRspPacket), @@ -46,7 +46,7 @@ pub enum UciResponse { DisableRsp, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum UciNotification { GenericError(GenericErrorPacket), DeviceStatusNtf(DeviceStatusNtfPacket), diff --git a/src/rust/uci/uci_logger.rs b/src/rust/uci/uci_logger.rs new file mode 100644 index 0000000..ad7ae0b --- /dev/null +++ b/src/rust/uci/uci_logger.rs @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crate::uci::UwbErr; +use async_trait::async_trait; +use bytes::{BufMut, BytesMut}; +use log::{error, info}; +use std::time::SystemTime; +use tokio::fs::{create_dir, rename, File, OpenOptions}; +use tokio::io::AsyncWriteExt; +use tokio::sync::Mutex; +use uwb_uci_packets::{ + AppConfigTlv, AppConfigTlvType, MessageType, Packet, SessionCommandChild, + SessionGetAppConfigRspBuilder, SessionResponseChild, SessionSetAppConfigCmdBuilder, + UciCommandChild, UciCommandPacket, UciNotificationPacket, UciPacketPacket, UciResponseChild, + UciResponsePacket, +}; + +// micros since 0000-01-01 +const UCI_EPOCH_DELTA: u64 = 0x00dcddb30f2f8000; +const MAX_FILE_SIZE: usize = 4096; +const PKT_LOG_HEADER_SIZE: usize = 25; +const VENDOR_ID: u64 = AppConfigTlvType::VendorId as u64; +const STATIC_STS_IV: u64 = AppConfigTlvType::StaticStsIv as u64; +const LOG_DIR: &str = "/data/misc/apexdata/com.android.uwb/log"; +const FILE_NAME: &str = "uwb_uci.log"; + +#[derive(Clone, PartialEq, Eq)] +pub enum UciLogMode { + Disabled, + Filtered, +} + +#[derive(Clone)] +enum Type { + Command = 1, + Response, + Notification, +} + +#[derive(Clone)] +struct UciLogConfig { + path: String, + max_file_size: usize, + mode: UciLogMode, +} + +impl UciLogConfig { + pub fn new(mode: UciLogMode) -> Self { + Self { path: format!("{}/{}", LOG_DIR, FILE_NAME), max_file_size: MAX_FILE_SIZE, mode } + } +} + +#[async_trait] +pub trait UciLogger { + async fn log_uci_command(&self, cmd: UciCommandPacket); + async fn log_uci_response(&self, rsp: UciResponsePacket); + async fn log_uci_notification(&self, ntf: UciNotificationPacket); + async fn close_file(&self); +} + +struct BufferedFile { + file: Option<File>, + size_count: usize, + buffer: BytesMut, +} + +pub struct UciLoggerImpl { + config: UciLogConfig, + buf_file: Mutex<BufferedFile>, +} + +impl UciLoggerImpl { + pub async fn new(mode: UciLogMode) -> Self { + let config = UciLogConfig::new(mode); + let file = match OpenOptions::new().append(true).open(&config.path).await.ok() { + Some(f) => Some(f), + None => { + if create_dir(LOG_DIR).await.is_err() { + error!("Failed to create dir"); + } + let new_file = match File::create(&config.path).await { + Ok(mut f) => { + if f.write_all(b"ucilogging").await.is_err() { + error!("failed to write"); + } + if f.flush().await.is_err() { + error!("Failed to flush"); + } + Some(f) + } + Err(e) => { + error!("Failed to create file {:?}", e); + None + } + }; + new_file + } + }; + let buf_file = BufferedFile { + size_count: match file { + Some(ref f) => f.metadata().await.unwrap().len().try_into().unwrap(), + None => 0, + }, + file, + buffer: BytesMut::new(), + }; + let ret = Self { config, buf_file: Mutex::new(buf_file) }; + info!("UCI logger created"); + ret + } + + async fn log_uci_packet(&self, packet: UciPacketPacket) { + let mt = packet.get_message_type(); + let bytes = packet.to_vec(); + let mt_byte = match mt { + MessageType::Command => Type::Command as u8, + MessageType::Response => Type::Response as u8, + MessageType::Notification => Type::Notification as u8, + }; + let flags = match mt { + MessageType::Command => 0b10, // down direction + MessageType::Response => 0b01, // up direction + MessageType::Notification => 0b01, // up direction + }; + let timestamp = u64::try_from( + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_micros(), + ) + .unwrap() + + UCI_EPOCH_DELTA; + + let length = u32::try_from(bytes.len()).unwrap() + 1; + + // Check whether exceeded the size limit + let mut buf_file = self.buf_file.lock().await; + if buf_file.size_count + bytes.len() + PKT_LOG_HEADER_SIZE > self.config.max_file_size { + match self.open_next_file().await { + Ok(()) => info!("New file created"), + Err(e) => error!("Open next file failed: {:?}", e), + } + } + buf_file.buffer.put_u32(length); // original length + buf_file.buffer.put_u32(length); // captured length + buf_file.buffer.put_u32(flags); // flags + buf_file.buffer.put_u32(0); // dropped packets + buf_file.buffer.put_u64(timestamp); // timestamp + buf_file.buffer.put_u8(mt_byte); // type + buf_file.buffer.put_slice(&bytes); // full packet. + buf_file.size_count += bytes.len() + PKT_LOG_HEADER_SIZE; + } + + async fn open_next_file(&self) -> Result<(), UwbErr> { + info!("Open next file"); + self.close_file().await; + if create_dir(LOG_DIR).await.is_err() { + error!("Failed to create dir"); + } + if rename(&self.config.path, self.config.path.clone() + ".last").await.is_err() { + error!("Failed to rename the file"); + } + let mut file = File::create(&self.config.path).await?; + file.write_all(b"ucilogging").await?; + if file.flush().await.is_err() { + error!("Failed to flush"); + } + self.buf_file.lock().await.file = Some(file); + Ok(()) + } +} + +#[async_trait] +impl UciLogger for UciLoggerImpl { + async fn log_uci_command(&self, cmd: UciCommandPacket) { + if let UciLogMode::Disabled = self.config.mode { + return; + } + // Only filter SET_APP_CONFIG_CMD + let filtered_cmd: UciCommandPacket = match cmd.specialize() { + UciCommandChild::SessionCommand(session_cmd) => match session_cmd.specialize() { + SessionCommandChild::SessionSetAppConfigCmd(set_config_cmd) => { + let session_id = set_config_cmd.get_session_id(); + let tlvs = set_config_cmd.get_tlvs(); + let mut filtered_tlvs = Vec::new(); + for tlv in tlvs { + if VENDOR_ID == tlv.cfg_id as u64 || STATIC_STS_IV == tlv.cfg_id as u64 { + filtered_tlvs + .push(AppConfigTlv { cfg_id: tlv.cfg_id, v: vec![0; tlv.v.len()] }); + } else { + filtered_tlvs.push(tlv.clone()); + } + } + SessionSetAppConfigCmdBuilder { session_id, tlvs: filtered_tlvs }.build().into() + } + _ => session_cmd.into(), + }, + _ => cmd, + }; + self.log_uci_packet(filtered_cmd.into()).await; + } + + async fn log_uci_response(&self, rsp: UciResponsePacket) { + if let UciLogMode::Disabled = self.config.mode { + return; + } + // Only filter GET_APP_CONFIG_RSP + let filtered_rsp: UciResponsePacket = match rsp.specialize() { + UciResponseChild::SessionResponse(session_rsp) => match session_rsp.specialize() { + SessionResponseChild::SessionGetAppConfigRsp(rsp) => { + let status = rsp.get_status(); + let tlvs = rsp.get_tlvs(); + let mut filtered_tlvs = Vec::new(); + for tlv in tlvs { + if VENDOR_ID == tlv.cfg_id as u64 || STATIC_STS_IV == tlv.cfg_id as u64 { + filtered_tlvs + .push(AppConfigTlv { cfg_id: tlv.cfg_id, v: vec![0; tlv.v.len()] }); + } else { + filtered_tlvs.push(tlv.clone()); + } + } + SessionGetAppConfigRspBuilder { status, tlvs: filtered_tlvs }.build().into() + } + _ => session_rsp.into(), + }, + _ => rsp, + }; + self.log_uci_packet(filtered_rsp.into()).await; + } + + async fn log_uci_notification(&self, ntf: UciNotificationPacket) { + if self.config.mode == UciLogMode::Disabled { + return; + } + // No notifications to be filtered. + self.log_uci_packet(ntf.into()).await; + } + + async fn close_file(&self) { + info!("UCI log file closing"); + let mut buf_file = self.buf_file.lock().await; + let buffer = buf_file.buffer.clone(); + if let Some(file) = &mut buf_file.file { + if file.write_all(&buffer).await.is_err() { + error!("Failed to write"); + } + if file.flush().await.is_err() { + error!("Failed to flush"); + } + buf_file.file = None; + buf_file.buffer.clear(); + } + buf_file.size_count = 0; + } +} + +#[cfg(test)] +pub struct MockUciLogger {} + +#[cfg(test)] +impl MockUciLogger { + pub fn new() -> Self { + MockUciLogger {} + } +} + +#[cfg(test)] +impl Default for MockUciLogger { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +#[async_trait] +impl UciLogger for MockUciLogger { + async fn log_uci_command(&self, _cmd: UciCommandPacket) {} + async fn log_uci_response(&self, _rsp: UciResponsePacket) {} + async fn log_uci_notification(&self, _ntf: UciNotificationPacket) {} + async fn close_file(&self) {} +} |