[bt][hfp] Add Cmer handling to AG Indicators procedure.
Updated procedure marker command matching to take the service level
connection initialization state into account.
Test: Added unit tests
Bug: 74312
Change-Id: I13dd223ce8d9b7a4362bc1d6688b0b5781daedd7
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/514490
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Fuchsia-Auto-Submit: Jeff Belgum <belgum@google.com>
Reviewed-by: Ani Ramakrishnan <aniramakri@google.com>
diff --git a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/peer/service_level_connection.rs b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/peer/service_level_connection.rs
index bf9d895..ba6bcc7 100644
--- a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/peer/service_level_connection.rs
+++ b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/peer/service_level_connection.rs
@@ -473,7 +473,7 @@
// Otherwise, try to match it to a procedure - it must be a non SLCI command since
// the channel has already been initialized.
- match ProcedureMarker::match_command(command) {
+ match ProcedureMarker::match_command(command, self.initialized()) {
Ok(ProcedureMarker::SlcInitialization) => {
warn!("Received unexpected SLCI command after SLC initialization: {:?}", command);
Err(command.into())
diff --git a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure.rs b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure.rs
index c23da1d..d29f358 100644
--- a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure.rs
+++ b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure.rs
@@ -207,18 +207,21 @@
}
}
- /// Matches the HF `command` to a procedure. Returns an error if the command is
- /// unable to be matched.
- pub fn match_command(command: &at::Command) -> Result<Self, ProcedureError> {
+ /// Matches the HF `command` to a procedure. `initialized` represents the initialization state
+ /// of the Service Level Connection.
+ ///
+ /// Returns an error if the command is unable to be matched.
+ pub fn match_command(command: &at::Command, initialized: bool) -> Result<Self, ProcedureError> {
match command {
at::Command::Brsf { .. }
| at::Command::Bac { .. }
| at::Command::CindTest { .. }
| at::Command::CindRead { .. }
- | at::Command::Cmer { .. }
| at::Command::ChldTest { .. }
| at::Command::BindTest { .. }
| at::Command::BindRead { .. } => Ok(Self::SlcInitialization),
+ at::Command::Cmer { .. } if initialized => Ok(Self::Indicators),
+ at::Command::Cmer { .. } => Ok(Self::SlcInitialization),
at::Command::Nrec { .. } => Ok(Self::Nrec),
at::Command::Cops { .. } | at::Command::CopsRead { .. } => {
Ok(Self::QueryOperatorSelection)
@@ -530,4 +533,15 @@
if messages == vec![at::Response::Ok, at::Response::Error]
);
}
+
+ #[test]
+ fn match_conditional_commands_based_on_slci() {
+ let command = at::Command::Cmer { mode: 3, keyp: 0, disp: 0, ind: 1 };
+ let marker = ProcedureMarker::match_command(&command, false).expect("command to match");
+ assert_eq!(marker, ProcedureMarker::SlcInitialization);
+
+ let command = at::Command::Cmer { mode: 3, keyp: 0, disp: 0, ind: 1 };
+ let marker = ProcedureMarker::match_command(&command, true).expect("command to match");
+ assert_eq!(marker, ProcedureMarker::Indicators);
+ }
}
diff --git a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/indicators_activation.rs b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/indicators_activation.rs
index 2d6c2427..3741ec3 100644
--- a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/indicators_activation.rs
+++ b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/indicators_activation.rs
@@ -4,7 +4,9 @@
use super::{AgUpdate, Procedure, ProcedureError, ProcedureMarker, ProcedureRequest};
-use crate::peer::service_level_connection::SlcState;
+use crate::{
+ peer::service_level_connection::SlcState, protocol::indicators::AgIndicatorsReporting,
+};
use at_commands as at;
/// Converts the indicator activeness flags (represented as Strings) to a vector of
@@ -61,6 +63,16 @@
AgUpdate::Error.into()
}
}
+ (false, at::Command::Cmer { mode, ind, .. }) => {
+ self.terminated = true;
+ if mode == AgIndicatorsReporting::EVENT_REPORTING_MODE
+ && state.ag_indicator_events_reporting.set_reporting_status(ind).is_ok()
+ {
+ AgUpdate::Ok.into()
+ } else {
+ AgUpdate::Error.into()
+ }
+ }
(_, update) => ProcedureRequest::Error(ProcedureError::UnexpectedHf(update)),
}
}
@@ -73,7 +85,6 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::protocol::indicators::AgIndicatorsReporting;
use matches::assert_matches;
#[test]
diff --git a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/slc_initialization.rs b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/slc_initialization.rs
index 38d3331..ea3e0ac 100644
--- a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/slc_initialization.rs
+++ b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/procedure/slc_initialization.rs
@@ -11,18 +11,12 @@
peer::service_level_connection::SlcState,
protocol::{
features::{AgFeatures, HfFeatures},
- indicators::AgIndicators,
+ indicators::{AgIndicators, AgIndicatorsReporting},
},
};
use {at_commands as at, num_traits::FromPrimitive};
-/// This mode's behavior is to forward unsolicited result codes directly per
-/// 3GPP TS 27.007 version 6.8.0, Section 8.10.
-/// This is the only supported mode in the Event Reporting Enabling AT command (AT+CMER).
-/// Defined in HFP v1.8 Section 4.34.2.
-const SUPPORTED_EVENT_REPORTING_MODE: i64 = 3;
-
/// A singular state within the SLC Initialization Procedure.
pub trait SlcProcedureState {
/// Returns the next state in the procedure based on the current state and the given
@@ -258,17 +252,13 @@
// Ensure that the requested `mode` and `ind` values are valid per HFP v1.8 Section 4.34.2.
match update {
at::Command::Cmer { mode, ind, .. } => {
- let is_valid = mode == SUPPORTED_EVENT_REPORTING_MODE && (ind == 0 || ind == 1);
- if !is_valid {
- return SlcErrorState::invalid_hf_argument(update.clone());
- }
- if ind != 0 {
- state.ag_indicator_events_reporting.enable();
+ if mode == AgIndicatorsReporting::EVENT_REPORTING_MODE
+ && state.ag_indicator_events_reporting.set_reporting_status(ind).is_ok()
+ {
+ Box::new(AgIndicatorStatusEnableReceived { state: state.clone() })
} else {
- state.ag_indicator_events_reporting.disable();
+ SlcErrorState::invalid_hf_argument(update.clone())
}
-
- Box::new(AgIndicatorStatusEnableReceived { state: state.clone() })
}
m => SlcErrorState::unexpected_hf(m),
}
@@ -546,8 +536,12 @@
SlcInitProcedure::new_at_state(AgIndicatorStatusReceived { state: state.clone() });
// `ind` = 9 is invalid.
- let invalid_enable =
- at::Command::Cmer { mode: SUPPORTED_EVENT_REPORTING_MODE, keyp: 0, disp: 0, ind: 9 };
+ let invalid_enable = at::Command::Cmer {
+ mode: AgIndicatorsReporting::EVENT_REPORTING_MODE,
+ keyp: 0,
+ disp: 0,
+ ind: 9,
+ };
assert_matches!(
procedure.hf_update(invalid_enable, &mut state),
ProcedureRequest::Error(Error::InvalidHfArgument(_))
diff --git a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/protocol/indicators.rs b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/protocol/indicators.rs
index 22ee44f..f5aa041 100644
--- a/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/protocol/indicators.rs
+++ b/src/connectivity/bluetooth/profiles/bt-hfp-audio-gateway/src/protocol/indicators.rs
@@ -230,6 +230,24 @@
}
impl AgIndicatorsReporting {
+ /// This mode's behavior is to forward unsolicited result codes directly per
+ /// 3GPP TS 27.007 version 6.8.0, Section 8.10.
+ /// This is the only supported mode in the Event Reporting Enabling AT command (AT+CMER).
+ /// Defined in HFP v1.8 Section 4.34.2.
+ pub const EVENT_REPORTING_MODE: i64 = 3;
+
+ /// Enables or disables the indicators reporting state while maintaining current indicator
+ /// flags. Valid status values are 0 for disabled and 1 for enabled. Any other value returns an
+ /// UnsupportedReportingStatus error. See HFP v1.8 Section 4.34.2 AT+CMER.
+ pub fn set_reporting_status(&mut self, status: i64) -> Result<(), UnsupportedReportingStatus> {
+ match status {
+ 0 => self.is_enabled = false,
+ 1 => self.is_enabled = true,
+ _ => return Err(UnsupportedReportingStatus(status)),
+ }
+ Ok(())
+ }
+
#[cfg(test)]
pub fn set_signal(&mut self, toggle: bool) {
self.signal = toggle;
@@ -254,18 +272,6 @@
Self { is_enabled: false, service: true, signal: true, roam: true, batt_chg: true }
}
- /// Sets the indicators reporting state to enabled while maintaining current indicator
- /// flags.
- pub fn enable(&mut self) {
- self.is_enabled = true;
- }
-
- /// Sets the indicators reporting state to disabled while maintaining the current
- /// indicator flags.
- pub fn disable(&mut self) {
- self.is_enabled = false;
- }
-
/// Updates the indicators with any indicators specified in `flags`.
pub fn update_from_flags(&mut self, flags: Vec<Option<bool>>) {
for (idx, flag) in flags.into_iter().enumerate() {
@@ -308,6 +314,10 @@
}
}
+#[derive(Debug)]
+/// An error representing an unsupported reporting status value.
+pub struct UnsupportedReportingStatus(i64);
+
/// The Call Indicator as specified in HFP v1.8, Section 4.10.1
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Call {
@@ -647,12 +657,12 @@
// Toggling indicators reporting should preserve indicator values.
let expected1 = AgIndicatorsReporting { is_enabled: false, ..status.clone() };
- status.disable();
+ status.set_reporting_status(0).unwrap();
assert_eq!(status, expected1);
- status.disable();
+ status.set_reporting_status(0).unwrap();
assert_eq!(status, expected1);
- status.enable();
+ status.set_reporting_status(1).unwrap();
let expected2 = AgIndicatorsReporting { is_enabled: true, ..expected1.clone() };
assert_eq!(status, expected2);
assert!(status.is_enabled);