diff options
author | Chih-Yu Huang <akahuang@google.com> | 2022-04-21 01:23:45 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-04-21 01:23:45 +0000 |
commit | 461cd66ed9b9a273e5e2ae3cd639621e22b294b2 (patch) | |
tree | d4caca295f839eca20e9508a216a7500c0387ffb | |
parent | d21957a715591a1687f0aa45eb3c242af8b0bd2b (diff) | |
parent | 20ed1544f2472ead6e454937a03c80886c09e665 (diff) | |
download | uwb-461cd66ed9b9a273e5e2ae3cd639621e22b294b2.tar.gz |
uwb_core: pass AppConfigParams when initiating session am: 20ed1544f2
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/uwb/+/17754006
Change-Id: Ia371c81a423d380c6bea09ea4b8317f9c7c3679c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | src/rust/uwb_core/src/session.rs | 2 | ||||
-rw-r--r-- | src/rust/uwb_core/src/session/error.rs | 6 | ||||
-rw-r--r-- | src/rust/uwb_core/src/session/params.rs | 36 | ||||
-rw-r--r-- | src/rust/uwb_core/src/session/session_manager.rs | 177 | ||||
-rw-r--r-- | src/rust/uwb_core/src/session/uwb_session.rs | 170 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/mock_uci_manager.rs | 22 |
6 files changed, 356 insertions, 57 deletions
diff --git a/src/rust/uwb_core/src/session.rs b/src/rust/uwb_core/src/session.rs index b3823d1..e621026 100644 --- a/src/rust/uwb_core/src/session.rs +++ b/src/rust/uwb_core/src/session.rs @@ -17,6 +17,8 @@ // TODO(akahuang): remove it after implementing the client of each component. #![allow(dead_code)] +mod uwb_session; + pub(crate) mod error; pub(crate) mod params; pub(crate) mod session_manager; diff --git a/src/rust/uwb_core/src/session/error.rs b/src/rust/uwb_core/src/session/error.rs index fc3b97e..eacd03a 100644 --- a/src/rust/uwb_core/src/session/error.rs +++ b/src/rust/uwb_core/src/session/error.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::uci::params::SessionId; +use crate::uci::params::{SessionId, SessionState}; #[derive(Debug, thiserror::Error, PartialEq, Eq)] pub enum Error { @@ -26,6 +26,10 @@ pub enum Error { DuplicatedSessionId(SessionId), #[error("Unknown SessionId: {0}")] UnknownSessionId(SessionId), + #[error("Invalid arguments")] + InvalidArguments, + #[error("Wrong SessionState: {0}")] + WrongState(SessionState), } pub type Result<T> = std::result::Result<T, Error>; diff --git a/src/rust/uwb_core/src/session/params.rs b/src/rust/uwb_core/src/session/params.rs index 00e533d..bda7c1a 100644 --- a/src/rust/uwb_core/src/session/params.rs +++ b/src/rust/uwb_core/src/session/params.rs @@ -12,4 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod fira_app_config_params; +pub mod fira_app_config_params; + +use crate::uci::params::{AppConfigTlv, SessionType}; + +/// The parameters of the UWB session. +// TODO(akahuang): Add CCC support. +#[derive(Debug, Clone)] +pub enum AppConfigParams { + Fira(fira_app_config_params::FiraAppConfigParams), +} + +impl AppConfigParams { + pub fn generate_tlvs(&self) -> Vec<AppConfigTlv> { + match self { + Self::Fira(params) => params.generate_tlvs(), + } + } + + pub fn generate_updated_tlvs(&self, prev_params: &Self) -> Vec<AppConfigTlv> { + match self { + Self::Fira(params) => match prev_params { + Self::Fira(prev_params) => params.generate_updated_tlvs(prev_params), + }, + } + } + + pub fn is_type_matched(&self, session_type: SessionType) -> bool { + match self { + Self::Fira(_) => { + session_type == SessionType::FiraDataTransfer + || session_type == SessionType::FiraRangingSession + } + } + } +} diff --git a/src/rust/uwb_core/src/session/session_manager.rs b/src/rust/uwb_core/src/session/session_manager.rs index be18f0c..60ab908 100644 --- a/src/rust/uwb_core/src/session/session_manager.rs +++ b/src/rust/uwb_core/src/session/session_manager.rs @@ -14,12 +14,14 @@ use std::collections::BTreeMap; -use log::{debug, error}; +use log::{debug, error, warn}; use tokio::sync::{mpsc, oneshot}; use crate::session::error::{Error, Result}; +use crate::session::params::AppConfigParams; +use crate::session::uwb_session::UwbSession; use crate::uci::notification::UciNotification; -use crate::uci::params::{SessionId, SessionType}; +use crate::uci::params::{SessionId, SessionState, SessionType}; use crate::uci::uci_manager::UciManager; const MAX_SESSION_COUNT: usize = 5; @@ -48,8 +50,14 @@ impl SessionManager { &mut self, session_id: SessionId, session_type: SessionType, + params: AppConfigParams, ) -> Result<()> { - self.send_cmd(SessionCommand::InitSession { session_id, session_type }).await + let result = + self.send_cmd(SessionCommand::InitSession { session_id, session_type, params }).await; + if result.is_err() && result != Err(Error::DuplicatedSessionId(session_id)) { + let _ = self.deinit_session(session_id).await; + } + result } async fn deinit_session(&mut self, session_id: SessionId) -> Result<()> { @@ -98,59 +106,86 @@ impl<T: UciManager> SessionManagerActor<T> { break; }, Some((cmd, result_sender)) => { - let result = self.handle_cmd(cmd).await; - let _ = result_sender.send(result); + self.handle_cmd(cmd, result_sender); } } } Some(uci_notf) = self.uci_notf_receiver.recv() => { - self.handle_uci_notification(uci_notf).await; + self.handle_uci_notification(uci_notf); } } } } - async fn handle_cmd(&mut self, cmd: SessionCommand) -> Result<()> { + fn handle_cmd(&mut self, cmd: SessionCommand, result_sender: oneshot::Sender<Result<()>>) { match cmd { - SessionCommand::InitSession { session_id, session_type } => { - if self.active_sessions.len() == MAX_SESSION_COUNT { - return Err(Error::MaxSessionsExceeded); - } + SessionCommand::InitSession { session_id, session_type, params } => { if self.active_sessions.contains_key(&session_id) { - return Err(Error::DuplicatedSessionId(session_id)); + let _ = result_sender.send(Err(Error::DuplicatedSessionId(session_id))); + return; } - if let Err(e) = self.uci_manager.session_init(session_id, session_type).await { - error!("Failed to init session: {:?}", e); - return Err(Error::Uci); + if self.active_sessions.len() == MAX_SESSION_COUNT { + let _ = result_sender.send(Err(Error::MaxSessionsExceeded)); + return; } - self.active_sessions.insert(session_id, UwbSession {}); - } + if !params.is_type_matched(session_type) { + error!("session_type {:?} doesn't match with the params", session_type); + let _ = result_sender.send(Err(Error::InvalidArguments)); + return; + } + let mut session = + UwbSession::new(self.uci_manager.clone(), session_id, session_type); + session.initialize(params, result_sender); + + // We store the session first. If the initialize() fails, then SessionManager will + // call deinit_session() to remove it. + self.active_sessions.insert(session_id, session); + } SessionCommand::DeinitSession { session_id } => { - if self.active_sessions.remove(&session_id).is_none() { - return Err(Error::UnknownSessionId(session_id)); + match self.active_sessions.remove(&session_id) { + None => { + let _ = result_sender.send(Err(Error::UnknownSessionId(session_id))); + } + Some(mut session) => { + session.deinitialize(result_sender); + } } + } + } + } - if let Err(e) = self.uci_manager.session_deinit(session_id).await { - error!("Failed to deinit session: {:?}", e); - return Err(Error::Uci); + fn handle_uci_notification(&mut self, notf: UciNotification) { + // TODO(akahuang): Remove this after handling multiple kind of notifications. + #[allow(clippy::single_match)] + match notf { + UciNotification::SessionStatus { session_id, session_state, reason_code } => { + if session_state == SessionState::SessionStateDeinit { + debug!("Session {:?} is deinitialized", session_id); + let _ = self.active_sessions.remove(&session_id); + return; + } + + match self.active_sessions.get_mut(&session_id) { + Some(session) => session.set_state(session_state), + None => { + warn!( + "Received notification of the unknown Session {:?}: {:?}, {:?}", + session_id, session_state, reason_code + ); + } } } + _ => {} } - Ok(()) } - - async fn handle_uci_notification(&mut self, _notf: UciNotification) {} } -// TODO(akahuang): store the necessary session parameters here. -struct UwbSession {} - #[derive(Debug)] enum SessionCommand { - InitSession { session_id: SessionId, session_type: SessionType }, + InitSession { session_id: SessionId, session_type: SessionType, params: AppConfigParams }, DeinitSession { session_id: SessionId }, } @@ -158,12 +193,32 @@ enum SessionCommand { mod tests { use super::*; + use crate::session::params::fira_app_config_params::*; + use crate::uci::error::StatusCode; use crate::uci::mock_uci_manager::MockUciManager; + use crate::uci::params::{ReasonCode, SetAppConfigResponse}; + use crate::utils::init_test_logging; + + fn generate_params() -> AppConfigParams { + AppConfigParams::Fira( + FiraAppConfigParamsBuilder::new() + .device_type(DeviceType::Controller) + .multi_node_mode(MultiNodeMode::Unicast) + .device_mac_address(UwbAddress::Short([1, 2])) + .dst_mac_address(vec![UwbAddress::Short([3, 4])]) + .device_role(DeviceRole::Initiator) + .vendor_id([0xFE, 0xDC]) + .static_sts_iv([0xDF, 0xCE, 0xAB, 0x12, 0x34, 0x56]) + .build() + .unwrap(), + ) + } async fn setup_session_manager<F>(setup_uci_manager_fn: F) -> (SessionManager, MockUciManager) where F: FnOnce(&mut MockUciManager), { + init_test_logging(); let (notf_sender, notf_receiver) = mpsc::unbounded_channel(); let mut uci_manager = MockUciManager::new(); uci_manager.expect_open_hal(vec![], Ok(())); @@ -173,47 +228,65 @@ mod tests { } #[tokio::test] - async fn test_init_session() { + async fn test_init_deinit_session() { let session_id = 0x123; let session_type = SessionType::FiraRangingSession; + let params = generate_params(); + let tlvs = params.generate_tlvs(); let session_id_clone = session_id; let session_type_clone = session_type; let (mut session_manager, mut mock_uci_manager) = setup_session_manager(move |uci_manager| { - uci_manager.expect_session_init(session_id_clone, session_type_clone, Ok(())); - }) - .await; - - let result = session_manager.init_session(session_id, session_type).await; - assert_eq!(result, Ok(())); - let result = session_manager.init_session(session_id, session_type).await; - assert_eq!(result, Err(Error::DuplicatedSessionId(session_id))); - assert!(mock_uci_manager.wait_expected_calls_done().await); - } - - #[tokio::test] - async fn test_deinit_session() { - let session_id = 0x123; - let session_type = SessionType::FiraRangingSession; - - let session_id_clone = session_id; - let session_type_clone = session_type; - let (mut session_manager, mut mock_uci_manager) = - setup_session_manager(move |uci_manager| { - uci_manager.expect_session_init(session_id_clone, session_type_clone, Ok(())); + let init_notfs = vec![UciNotification::SessionStatus { + session_id, + session_state: SessionState::SessionStateInit, + reason_code: ReasonCode::StateChangeWithSessionManagementCommands, + }]; + let set_app_config_notfs = vec![UciNotification::SessionStatus { + session_id, + session_state: SessionState::SessionStateIdle, + reason_code: ReasonCode::StateChangeWithSessionManagementCommands, + }]; + uci_manager.expect_session_init( + session_id_clone, + session_type_clone, + init_notfs, + Ok(()), + ); + uci_manager.expect_session_set_app_config( + session_id_clone, + tlvs, + set_app_config_notfs, + Ok(SetAppConfigResponse { + status: StatusCode::UciStatusOk, + config_status: vec![], + }), + ); uci_manager.expect_session_deinit(session_id_clone, Ok(())); }) .await; + // Deinit a session before initialized should fail. let result = session_manager.deinit_session(session_id).await; assert_eq!(result, Err(Error::UnknownSessionId(session_id))); - let result = session_manager.init_session(session_id, session_type).await; + + // Initialize a normal session should be successful. + let result = session_manager.init_session(session_id, session_type, params.clone()).await; assert_eq!(result, Ok(())); + + // Initialize a session multiple times without deinitialize should fail. + let result = session_manager.init_session(session_id, session_type, params).await; + assert_eq!(result, Err(Error::DuplicatedSessionId(session_id))); + + // Deinitialize the session should be successful. let result = session_manager.deinit_session(session_id).await; assert_eq!(result, Ok(())); + + // Deinit a session after deinitialized should fail. let result = session_manager.deinit_session(session_id).await; assert_eq!(result, Err(Error::UnknownSessionId(session_id))); + assert!(mock_uci_manager.wait_expected_calls_done().await); } } diff --git a/src/rust/uwb_core/src/session/uwb_session.rs b/src/rust/uwb_core/src/session/uwb_session.rs new file mode 100644 index 0000000..93f894b --- /dev/null +++ b/src/rust/uwb_core/src/session/uwb_session.rs @@ -0,0 +1,170 @@ +// Copyright 2022, 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 log::{debug, error, warn}; +use tokio::sync::{mpsc, oneshot, watch}; + +use crate::session::error::{Error, Result}; +use crate::session::params::AppConfigParams; +use crate::uci::error::StatusCode; +use crate::uci::params::{SessionId, SessionState, SessionType}; +use crate::uci::uci_manager::UciManager; + +pub(crate) struct UwbSession { + cmd_sender: mpsc::UnboundedSender<(Command, oneshot::Sender<Result<()>>)>, + state_sender: watch::Sender<SessionState>, +} + +impl UwbSession { + pub fn new<T: UciManager>( + uci_manager: T, + session_id: SessionId, + session_type: SessionType, + ) -> Self { + let (cmd_sender, cmd_receiver) = mpsc::unbounded_channel(); + let (state_sender, mut state_receiver) = watch::channel(SessionState::SessionStateDeinit); + // Mark the initial value of state as seen. + let _ = state_receiver.borrow_and_update(); + + let mut actor = UwbSessionActor::new( + cmd_receiver, + state_receiver, + uci_manager, + session_id, + session_type, + ); + tokio::spawn(async move { actor.run().await }); + + Self { cmd_sender, state_sender } + } + + pub fn initialize( + &mut self, + params: AppConfigParams, + result_sender: oneshot::Sender<Result<()>>, + ) { + let _ = self.cmd_sender.send((Command::Initialize { params }, result_sender)); + } + + pub fn deinitialize(&mut self, result_sender: oneshot::Sender<Result<()>>) { + let _ = self.cmd_sender.send((Command::Deinitialize, result_sender)); + } + + pub fn set_state(&mut self, state: SessionState) { + let _ = self.state_sender.send(state); + } +} + +struct UwbSessionActor<T: UciManager> { + cmd_receiver: mpsc::UnboundedReceiver<(Command, oneshot::Sender<Result<()>>)>, + state_receiver: watch::Receiver<SessionState>, + uci_manager: T, + session_id: SessionId, + session_type: SessionType, +} + +impl<T: UciManager> UwbSessionActor<T> { + fn new( + cmd_receiver: mpsc::UnboundedReceiver<(Command, oneshot::Sender<Result<()>>)>, + state_receiver: watch::Receiver<SessionState>, + uci_manager: T, + session_id: SessionId, + session_type: SessionType, + ) -> Self { + Self { cmd_receiver, state_receiver, uci_manager, session_id, session_type } + } + + async fn run(&mut self) { + loop { + tokio::select! { + cmd = self.cmd_receiver.recv() => { + match cmd { + None => { + debug!("UwbSession is about to drop."); + break; + }, + Some((cmd, result_sender)) => { + let result = match cmd { + Command::Initialize { params } => self.initialize(params).await, + Command::Deinitialize => self.deinitialize().await, + }; + let _ = result_sender.send(result); + } + } + } + } + } + } + + async fn initialize(&mut self, params: AppConfigParams) -> Result<()> { + if let Err(e) = self.uci_manager.session_init(self.session_id, self.session_type).await { + error!("Failed to initialize session: {:?}", e); + return Err(Error::Uci); + } + self.wait_state(SessionState::SessionStateInit).await?; + + let tlvs = params.generate_tlvs(); + match self.uci_manager.session_set_app_config(self.session_id, tlvs).await { + Ok(result) => { + for config_status in result.config_status.iter() { + warn!( + "AppConfig {:?} is not applied: {:?}", + config_status.cfg_id, config_status.status + ); + } + if result.status != StatusCode::UciStatusOk { + error!("Failed to set app_config. StatusCode: {:?}", result.status); + return Err(Error::Uci); + } + } + Err(e) => { + error!("Failed to set app_config: {:?}", e); + return Err(Error::Uci); + } + } + self.wait_state(SessionState::SessionStateIdle).await?; + + Ok(()) + } + + async fn deinitialize(&mut self) -> Result<()> { + if let Err(e) = self.uci_manager.session_deinit(self.session_id).await { + error!("Failed to deinit session: {:?}", e); + return Err(Error::Uci); + } + Ok(()) + } + + async fn wait_state(&mut self, expected_state: SessionState) -> Result<()> { + if self.state_receiver.changed().await.is_err() { + debug!("UwbSession is about to drop."); + return Err(Error::TokioFailure); + } + let state = *self.state_receiver.borrow(); + if state != expected_state { + error!( + "Transit to wrong Session state {:?}. The expected state is {:?}", + state, expected_state + ); + return Err(Error::WrongState(state)); + } + + Ok(()) + } +} + +enum Command { + Initialize { params: AppConfigParams }, + Deinitialize, +} diff --git a/src/rust/uwb_core/src/uci/mock_uci_manager.rs b/src/rust/uwb_core/src/uci/mock_uci_manager.rs index 05af30c..c69dc51 100644 --- a/src/rust/uwb_core/src/uci/mock_uci_manager.rs +++ b/src/rust/uwb_core/src/uci/mock_uci_manager.rs @@ -102,11 +102,13 @@ impl MockUciManager { &mut self, expected_session_id: SessionId, expected_session_type: SessionType, + notfs: Vec<UciNotification>, out: Result<()>, ) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::SessionInit { expected_session_id, expected_session_type, + notfs, out, }); } @@ -122,11 +124,13 @@ impl MockUciManager { &mut self, expected_session_id: SessionId, expected_config_tlvs: Vec<AppConfigTlv>, + notfs: Vec<UciNotification>, out: Result<SetAppConfigResponse>, ) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::SessionSetAppConfig { expected_session_id, expected_config_tlvs, + notfs, out, }); } @@ -365,10 +369,16 @@ impl UciManager for MockUciManager { ) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { - Some(ExpectedCall::SessionInit { expected_session_id, expected_session_type, out }) - if expected_session_id == session_id && expected_session_type == session_type => - { + Some(ExpectedCall::SessionInit { + expected_session_id, + expected_session_type, + notfs, + out, + }) if expected_session_id == session_id && expected_session_type == session_type => { self.expect_call_consumed.notify_one(); + for notf in notfs.into_iter() { + let _ = self.notf_sender.as_mut().unwrap().send(notf); + } out } Some(call) => { @@ -406,11 +416,15 @@ impl UciManager for MockUciManager { Some(ExpectedCall::SessionSetAppConfig { expected_session_id, expected_config_tlvs, + notfs, out, }) if expected_session_id == session_id && app_config_tlvs_eq(&expected_config_tlvs, &config_tlvs) => { self.expect_call_consumed.notify_one(); + for notf in notfs.into_iter() { + let _ = self.notf_sender.as_mut().unwrap().send(notf); + } out } Some(call) => { @@ -645,6 +659,7 @@ enum ExpectedCall { SessionInit { expected_session_id: SessionId, expected_session_type: SessionType, + notfs: Vec<UciNotification>, out: Result<()>, }, SessionDeinit { @@ -654,6 +669,7 @@ enum ExpectedCall { SessionSetAppConfig { expected_session_id: SessionId, expected_config_tlvs: Vec<AppConfigTlv>, + notfs: Vec<UciNotification>, out: Result<SetAppConfigResponse>, }, SessionGetAppConfig { |