summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChih-Yu Huang <akahuang@google.com>2022-04-21 01:23:45 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-04-21 01:23:45 +0000
commit461cd66ed9b9a273e5e2ae3cd639621e22b294b2 (patch)
treed4caca295f839eca20e9508a216a7500c0387ffb
parentd21957a715591a1687f0aa45eb3c242af8b0bd2b (diff)
parent20ed1544f2472ead6e454937a03c80886c09e665 (diff)
downloaduwb-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.rs2
-rw-r--r--src/rust/uwb_core/src/session/error.rs6
-rw-r--r--src/rust/uwb_core/src/session/params.rs36
-rw-r--r--src/rust/uwb_core/src/session/session_manager.rs177
-rw-r--r--src/rust/uwb_core/src/session/uwb_session.rs170
-rw-r--r--src/rust/uwb_core/src/uci/mock_uci_manager.rs22
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 {