| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use {at_commands as at, core::fmt::Debug, fidl_fuchsia_bluetooth_hfp::CallState}; |
| |
| use crate::procedure::AgUpdate; |
| |
| /// This implementation supports the 7 indicators defined in HFP v1.8 Section 4.35. |
| /// The indices of these indicators are fixed. |
| pub(crate) const SERVICE_INDICATOR_INDEX: usize = 1; |
| pub(crate) const CALL_INDICATOR_INDEX: usize = 2; |
| pub(crate) const CALL_SETUP_INDICATOR_INDEX: usize = 3; |
| pub(crate) const CALL_HELD_INDICATOR_INDEX: usize = 4; |
| pub(crate) const SIGNAL_INDICATOR_INDEX: usize = 5; |
| pub(crate) const ROAM_INDICATOR_INDEX: usize = 6; |
| pub(crate) const BATT_CHG_INDICATOR_INDEX: usize = 7; |
| |
| /// The supported HF indicators. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum HfIndicator { |
| EnhancedSafety(bool), |
| BatteryLevel(u8), |
| } |
| |
| /// A single indicator status + value. |
| #[derive(Clone, Copy, Debug)] |
| struct Indicator<T: Clone + Copy + Debug> { |
| /// Whether this indicator is enabled or not. |
| enabled: bool, |
| /// The value of the indicator. |
| value: Option<T>, |
| } |
| |
| impl<T: Clone + Copy + Debug> Default for Indicator<T> { |
| fn default() -> Self { |
| Self { enabled: false, value: None } |
| } |
| } |
| |
| /// The supported HF indicators and their enabled/disabled status & values. |
| /// Defined in HFP v1.8 Section 4.36. |
| #[derive(Clone, Copy, Debug, Default)] |
| pub struct HfIndicators { |
| /// The Enhanced Safety HF indicator. There are only two potential values (enabled, disabled). |
| enhanced_safety: Indicator<bool>, |
| /// The Battery Level HF indicator. Can be any integer value between [0, 100]. |
| battery_level: Indicator<u8>, |
| } |
| |
| impl HfIndicators { |
| /// The Maximum Battery Level value for the `battery_level` indicator. |
| /// Defined in HFP v1.8 Section 4.35. |
| const MAX_BATTERY_LEVEL: u8 = 100; |
| |
| /// Enables the supported HF indicators based on the provided AT `indicators`. |
| pub fn enable_indicators(&mut self, indicators: Vec<at::BluetoothHFIndicator>) { |
| for ind in indicators { |
| if ind == at::BluetoothHFIndicator::EnhancedSafety { |
| self.enhanced_safety.enabled = true; |
| } |
| if ind == at::BluetoothHFIndicator::BatteryLevel { |
| self.battery_level.enabled = true; |
| } |
| } |
| } |
| |
| /// Updates the `indicator` with the provided `value`. |
| /// Returns Error if the indicator is disabled or if the `value` is out of bounds. |
| /// Returns a valid HfIndicator on success. |
| pub fn update_indicator_value( |
| &mut self, |
| indicator: at::BluetoothHFIndicator, |
| value: i64, |
| ) -> Result<HfIndicator, ()> { |
| let ind = match indicator { |
| at::BluetoothHFIndicator::EnhancedSafety if self.enhanced_safety.enabled => { |
| if value != 0 && value != 1 { |
| return Err(()); |
| } |
| let v = value != 0; |
| self.enhanced_safety.value = Some(v); |
| HfIndicator::EnhancedSafety(v) |
| } |
| at::BluetoothHFIndicator::BatteryLevel if self.battery_level.enabled => { |
| if value < 0 || value > Self::MAX_BATTERY_LEVEL.into() { |
| return Err(()); |
| } |
| let v = value as u8; |
| self.battery_level.value = Some(v); |
| HfIndicator::BatteryLevel(v) |
| } |
| ind => { |
| log::warn!("Received HF indicator update for disabled indicator: {:?}", ind); |
| return Err(()); |
| } |
| }; |
| Ok(ind) |
| } |
| |
| /// Returns the +BIND response for the current HF indicator status. |
| pub fn bind_response(&self) -> Vec<at::Response> { |
| vec![ |
| at::success(at::Success::BindStatus { |
| anum: at::BluetoothHFIndicator::EnhancedSafety, |
| state: self.enhanced_safety.enabled, |
| }), |
| at::success(at::Success::BindStatus { |
| anum: at::BluetoothHFIndicator::BatteryLevel, |
| state: self.battery_level.enabled, |
| }), |
| at::Response::Ok, |
| ] |
| } |
| } |
| |
| /// A collection of indicators supported by the AG. |
| #[derive(Debug, Default, Clone, Copy)] |
| pub struct AgIndicators { |
| pub service: bool, |
| pub call: Call, |
| pub callsetup: CallSetup, |
| pub callheld: CallHeld, |
| pub signal: u8, |
| pub roam: bool, |
| pub battchg: u8, |
| } |
| |
| /// The supported phone status update indicators. |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub enum AgIndicator { |
| Service(u8), |
| Call(u8), |
| CallSetup(u8), |
| CallHeld(u8), |
| Signal(u8), |
| Roam(u8), |
| BatteryLevel(u8), |
| } |
| |
| impl From<AgIndicator> for at::Response { |
| fn from(src: AgIndicator) -> at::Response { |
| match src { |
| AgIndicator::Service(v) => at::Response::Success(at::Success::Ciev { |
| ind: SERVICE_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| AgIndicator::Call(v) => at::Response::Success(at::Success::Ciev { |
| ind: CALL_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| AgIndicator::CallSetup(v) => at::Response::Success(at::Success::Ciev { |
| ind: CALL_SETUP_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| AgIndicator::CallHeld(v) => at::Response::Success(at::Success::Ciev { |
| ind: CALL_HELD_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| AgIndicator::Signal(v) => at::Response::Success(at::Success::Ciev { |
| ind: SIGNAL_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| AgIndicator::Roam(v) => at::Response::Success(at::Success::Ciev { |
| ind: ROAM_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| AgIndicator::BatteryLevel(v) => at::Response::Success(at::Success::Ciev { |
| ind: BATT_CHG_INDICATOR_INDEX as i64, |
| value: v as i64, |
| }), |
| } |
| } |
| } |
| |
| impl From<Call> for AgIndicator { |
| fn from(src: Call) -> Self { |
| Self::Call(src as u8) |
| } |
| } |
| |
| impl From<CallSetup> for AgIndicator { |
| fn from(src: CallSetup) -> Self { |
| Self::CallSetup(src as u8) |
| } |
| } |
| |
| impl From<CallHeld> for AgIndicator { |
| fn from(src: CallHeld) -> Self { |
| Self::CallHeld(src as u8) |
| } |
| } |
| |
| impl From<AgIndicator> for AgUpdate { |
| fn from(src: AgIndicator) -> Self { |
| Self::PhoneStatusIndicator(src) |
| } |
| } |
| |
| /// The current AG Indicators Reporting status with the active/inactive status of each |
| /// supported indicator. |
| /// |
| /// Per HFP v1.8 Section 4.35, the Call, Call Setup, and Call Held indicators |
| /// must always be activated; these fields are read-only and will always |
| /// be set and therefore are omitted from the `AgIndicatorsReporting` object. |
| /// |
| /// It is valid to toggle the activeness of specific event even if Indicators Reporting is |
| /// disabled. |
| /// |
| /// By default, all indicators are set to enabled. |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct AgIndicatorsReporting { |
| is_enabled: bool, |
| |
| /// The event indicators. |
| service: bool, |
| signal: bool, |
| roam: bool, |
| batt_chg: bool, |
| } |
| |
| impl AgIndicatorsReporting { |
| #[cfg(test)] |
| pub fn set_signal(&mut self, toggle: bool) { |
| self.signal = toggle; |
| } |
| |
| #[cfg(test)] |
| pub fn set_service(&mut self, toggle: bool) { |
| self.service = toggle; |
| } |
| |
| #[cfg(test)] |
| pub fn set_batt_chg(&mut self, toggle: bool) { |
| self.batt_chg = toggle; |
| } |
| |
| #[cfg(test)] |
| pub fn new_enabled() -> Self { |
| Self { is_enabled: true, service: true, signal: true, roam: true, batt_chg: true } |
| } |
| |
| pub fn new_disabled() -> Self { |
| 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() { |
| // The indicator indices are "1-indexed". |
| let index = idx + 1; |
| let toggle = if let Some(b) = flag { b } else { continue }; |
| // See HFP v1.8 Section 4.35 for the flags that cannot be toggled. |
| // Any indicators beyond the 7 supported in this implementation can be safely ignored. |
| match index { |
| SERVICE_INDICATOR_INDEX => self.service = toggle, |
| CALL_INDICATOR_INDEX | CALL_SETUP_INDICATOR_INDEX | CALL_HELD_INDICATOR_INDEX => (), |
| SIGNAL_INDICATOR_INDEX => self.signal = toggle, |
| ROAM_INDICATOR_INDEX => self.roam = toggle, |
| BATT_CHG_INDICATOR_INDEX => self.batt_chg = toggle, |
| _ => break, |
| } |
| } |
| } |
| |
| /// Returns true if indicators reporting is enabled and the provided `status` indicator should |
| /// be sent to the peer. |
| pub fn indicator_enabled(&self, status: &AgIndicator) -> bool { |
| self.is_enabled |
| && match status { |
| AgIndicator::Service(_) => self.service, |
| AgIndicator::Call(_) | AgIndicator::CallSetup(_) | AgIndicator::CallHeld(_) => true, |
| AgIndicator::Signal(_) => self.signal, |
| AgIndicator::Roam(_) => self.roam, |
| AgIndicator::BatteryLevel(_) => self.batt_chg, |
| } |
| } |
| } |
| |
| impl Default for AgIndicatorsReporting { |
| /// The default indicators reporting state is disabled. |
| /// Per HFP v1.8 Section 4.2.1.3, the HF will _always_ request to enable indicators |
| /// reporting in the SLCI procedure (See +CMER AT command). |
| fn default() -> Self { |
| Self::new_disabled() |
| } |
| } |
| |
| /// The Call Indicator as specified in HFP v1.8, Section 4.10.1 |
| #[derive(PartialEq, Clone, Copy, Debug)] |
| pub enum Call { |
| /// There are no calls present in the AG (active or held). |
| None, |
| /// There is at least one call present in the AG (active or held). |
| Some, |
| } |
| |
| impl Default for Call { |
| fn default() -> Self { |
| Self::None |
| } |
| } |
| |
| impl From<Call> for bool { |
| fn from(call: Call) -> Self { |
| match call { |
| Call::None => false, |
| Call::Some => true, |
| } |
| } |
| } |
| |
| impl From<bool> for Call { |
| fn from(call: bool) -> Self { |
| match call { |
| false => Self::None, |
| true => Self::Some, |
| } |
| } |
| } |
| |
| impl Call { |
| /// Find the Call state based on all the calls in `iter`. |
| pub fn find(mut iter: impl Iterator<Item = CallState>) -> Self { |
| iter.any(|state| [CallState::OngoingActive, CallState::OngoingHeld].contains(&state)).into() |
| } |
| } |
| |
| /// The Callsetup Indicator as specified in HFP v1.8, Section 4.10.2 |
| #[derive(PartialEq, Clone, Copy, Debug)] |
| pub enum CallSetup { |
| /// No call setup in progress. |
| None = 0, |
| /// Incoming call setup in progress. |
| Incoming = 1, |
| /// Outgoing call setup in dialing state. |
| OutgoingDialing = 2, |
| /// Outgoing call setup in alerting state. |
| OutgoingAlerting = 3, |
| } |
| |
| impl Default for CallSetup { |
| fn default() -> Self { |
| Self::None |
| } |
| } |
| |
| impl CallSetup { |
| /// Find CallSetup state based on the first call in `iter` that is in a callsetup state. |
| pub fn find(mut iter: impl Iterator<Item = CallState>) -> Self { |
| iter.find(|state| { |
| [ |
| CallState::IncomingRinging, |
| CallState::IncomingWaiting, |
| CallState::OutgoingAlerting, |
| CallState::OutgoingDialing, |
| ] |
| .contains(&state) |
| }) |
| .map(CallSetup::from) |
| .unwrap_or(CallSetup::None) |
| } |
| } |
| |
| impl From<CallState> for CallSetup { |
| fn from(state: CallState) -> Self { |
| match state { |
| CallState::IncomingRinging | CallState::IncomingWaiting => Self::Incoming, |
| CallState::OutgoingDialing => Self::OutgoingDialing, |
| CallState::OutgoingAlerting => Self::OutgoingAlerting, |
| _ => Self::None, |
| } |
| } |
| } |
| |
| /// The Callheld Indicator as specified in HFP v1.8, Section 4.10.3 |
| #[derive(PartialEq, Clone, Copy, Debug)] |
| pub enum CallHeld { |
| /// No calls held. |
| None = 0, |
| /// Call is placed on hold or active/held calls swapped (The AG has both an active AND a held |
| /// call). |
| HeldAndActive = 1, |
| /// Call on hold, no active call. |
| Held = 2, |
| } |
| |
| impl Default for CallHeld { |
| fn default() -> Self { |
| Self::None |
| } |
| } |
| |
| impl CallHeld { |
| /// Find the CallHeld state based on all calls in `iter`. |
| pub fn find(mut iter: impl Iterator<Item = CallState> + Clone) -> Self { |
| let any_held = iter.clone().any(|state| state == CallState::OngoingHeld); |
| let any_active = iter.any(|state| state == CallState::OngoingActive); |
| match (any_held, any_active) { |
| (true, false) => CallHeld::Held, |
| (true, true) => CallHeld::HeldAndActive, |
| (false, _) => CallHeld::None, |
| } |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Default)] |
| pub struct CallIndicators { |
| pub call: Call, |
| pub callsetup: CallSetup, |
| pub callheld: CallHeld, |
| } |
| |
| impl CallIndicators { |
| /// Find CallIndicators based on all items in `iter`. |
| pub fn find(iter: impl Iterator<Item = CallState> + Clone) -> Self { |
| let call = Call::find(iter.clone()); |
| let callsetup = CallSetup::find(iter.clone()); |
| let callheld = CallHeld::find(iter); |
| CallIndicators { call, callsetup, callheld } |
| } |
| |
| /// A list of all the statuses that have changed between `other` and self. |
| /// The values in the list are the values found in `self`. |
| pub fn difference(&self, other: Self) -> CallIndicatorsUpdates { |
| let mut changes = CallIndicatorsUpdates::default(); |
| if other.call != self.call { |
| changes.call = Some(self.call); |
| } |
| if other.callsetup != self.callsetup { |
| changes.callsetup = Some(self.callsetup); |
| } |
| if other.callheld != self.callheld { |
| changes.callheld = Some(self.callheld); |
| } |
| changes |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug, PartialEq, Default)] |
| pub struct CallIndicatorsUpdates { |
| pub call: Option<Call>, |
| pub callsetup: Option<CallSetup>, |
| pub callheld: Option<CallHeld>, |
| } |
| |
| impl CallIndicatorsUpdates { |
| /// Returns true if all fields are `None` |
| pub fn is_empty(&self) -> bool { |
| self.call.is_none() && self.callsetup.is_none() && self.callheld.is_none() |
| } |
| |
| /// Returns a Vec of all updated indicators. This vec is ordered by Indicator index. |
| pub fn to_vec(&self) -> Vec<AgIndicator> { |
| let mut v = vec![]; |
| v.extend(self.call.map(|i| AgIndicator::Call(i as u8))); |
| v.extend(self.callsetup.map(|i| AgIndicator::CallSetup(i as u8))); |
| v.extend(self.callheld.map(|i| AgIndicator::CallHeld(i as u8))); |
| v |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use matches::assert_matches; |
| |
| #[test] |
| fn update_hf_indicators_with_invalid_values_is_error() { |
| let mut hf_indicators = HfIndicators::default(); |
| hf_indicators.enable_indicators(vec![ |
| at::BluetoothHFIndicator::BatteryLevel, |
| at::BluetoothHFIndicator::EnhancedSafety, |
| ]); |
| |
| let battery_too_low = -18; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::BatteryLevel, battery_too_low), |
| Err(()) |
| ); |
| let battery_too_high = 1243; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::BatteryLevel, battery_too_high), |
| Err(()) |
| ); |
| |
| let negative_safety = -1; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::EnhancedSafety, negative_safety), |
| Err(()) |
| ); |
| let large_safety = 8; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::EnhancedSafety, large_safety), |
| Err(()) |
| ); |
| } |
| |
| #[test] |
| fn update_disabled_hf_indicators_with_valid_values_is_error() { |
| // Default is no indicators set. Therefore any updates are errors. |
| let mut hf_indicators = HfIndicators::default(); |
| |
| let valid_battery = 32; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::BatteryLevel, valid_battery), |
| Err(()) |
| ); |
| |
| let valid_safety = 0; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::EnhancedSafety, valid_safety), |
| Err(()) |
| ); |
| } |
| |
| #[test] |
| fn update_hf_indicators_with_valid_values_is_ok() { |
| let mut hf_indicators = HfIndicators::default(); |
| // Default values. |
| assert_eq!(hf_indicators.enhanced_safety.value, None); |
| assert_eq!(hf_indicators.enhanced_safety.enabled, false); |
| assert_eq!(hf_indicators.battery_level.value, None); |
| assert_eq!(hf_indicators.battery_level.enabled, false); |
| |
| // Enable both. |
| hf_indicators.enable_indicators(vec![ |
| at::BluetoothHFIndicator::BatteryLevel, |
| at::BluetoothHFIndicator::EnhancedSafety, |
| ]); |
| |
| let valid_battery = 83; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::BatteryLevel, valid_battery), |
| Ok(HfIndicator::BatteryLevel(83)) |
| ); |
| assert_eq!(hf_indicators.battery_level.value, Some(valid_battery as u8)); |
| |
| let valid_safety = 0; |
| assert_matches!( |
| hf_indicators |
| .update_indicator_value(at::BluetoothHFIndicator::EnhancedSafety, valid_safety), |
| Ok(HfIndicator::EnhancedSafety(false)) |
| ); |
| assert_eq!(hf_indicators.enhanced_safety.value, Some(false)); |
| } |
| |
| #[test] |
| fn default_indicators_reporting_is_disabled_with_all_indicators_enabled() { |
| let default = AgIndicatorsReporting::default(); |
| assert!(!default.is_enabled); |
| assert!(default.service); |
| assert!(default.signal); |
| assert!(default.roam); |
| assert!(default.batt_chg); |
| } |
| |
| #[test] |
| fn indicator_flags_are_updated_from_bool_flags() { |
| // No flags is OK, no updates. |
| let mut status = AgIndicatorsReporting::default(); |
| let empty = vec![]; |
| let expected = status.clone(); |
| status.update_from_flags(empty); |
| assert_eq!(status, expected); |
| |
| // An incomplete set of flags is OK (5 out of 7 supplied). The Call, Call Held, Call Setup |
| // flags will not be overridden. |
| let mut status = AgIndicatorsReporting::default(); |
| let incomplete = vec![Some(false), None, Some(false), Some(true)]; |
| let expected = AgIndicatorsReporting { |
| is_enabled: false, |
| service: false, |
| signal: true, |
| roam: true, |
| batt_chg: true, |
| }; |
| status.update_from_flags(incomplete); |
| assert_eq!(status, expected); |
| |
| // A typical set of flags (all 7) is OK. |
| let mut status = AgIndicatorsReporting::default(); |
| let flags = |
| vec![None, Some(false), Some(false), Some(true), Some(false), Some(false), None]; |
| let expected = AgIndicatorsReporting { |
| is_enabled: false, |
| service: true, |
| signal: false, |
| roam: false, |
| batt_chg: true, |
| }; |
| status.update_from_flags(flags); |
| assert_eq!(status, expected); |
| |
| // Too many flags provided is also OK. Per the spec, this can happen, and the excess flags |
| // are gracefully ignored. |
| let mut status = AgIndicatorsReporting::default(); |
| let too_many = |
| vec![None, None, None, None, Some(true), Some(false), None, Some(true), Some(false)]; |
| let expected = AgIndicatorsReporting { |
| is_enabled: false, |
| service: true, |
| signal: true, |
| roam: false, |
| batt_chg: true, |
| }; |
| status.update_from_flags(too_many); |
| assert_eq!(status, expected); |
| } |
| |
| #[test] |
| fn toggling_indicators_reporting_maintains_same_indicators() { |
| let mut status = AgIndicatorsReporting::default(); |
| assert!(!status.is_enabled); |
| |
| // Unset a specific indicator. |
| status.set_batt_chg(false); |
| |
| // Toggling indicators reporting should preserve indicator values. |
| let expected1 = AgIndicatorsReporting { is_enabled: false, ..status.clone() }; |
| status.disable(); |
| assert_eq!(status, expected1); |
| status.disable(); |
| assert_eq!(status, expected1); |
| |
| status.enable(); |
| let expected2 = AgIndicatorsReporting { is_enabled: true, ..expected1.clone() }; |
| assert_eq!(status, expected2); |
| assert!(status.is_enabled); |
| } |
| |
| #[test] |
| fn indicator_enabled_is_false_when_indicators_reporting_disabled() { |
| let status = AgIndicatorsReporting::new_disabled(); |
| |
| // Even though all the individual indicator values are toggled on, we expect |
| // `indicator_enabled` to return false because indicators reporting is disabled. |
| assert!(!status.indicator_enabled(&AgIndicator::Service(0))); |
| assert!(!status.indicator_enabled(&AgIndicator::Call(0))); |
| } |
| |
| #[test] |
| fn indicator_enabled_check_returns_expected_result() { |
| let mut status = AgIndicatorsReporting::new_enabled(); |
| status.batt_chg = false; |
| status.roam = false; |
| |
| assert!(status.indicator_enabled(&AgIndicator::Service(0))); |
| assert!(status.indicator_enabled(&AgIndicator::Signal(0))); |
| assert!(status.indicator_enabled(&AgIndicator::Call(0))); |
| assert!(status.indicator_enabled(&AgIndicator::CallSetup(0))); |
| assert!(status.indicator_enabled(&AgIndicator::CallHeld(0))); |
| assert!(!status.indicator_enabled(&AgIndicator::Roam(0))); |
| assert!(!status.indicator_enabled(&AgIndicator::BatteryLevel(0))); |
| } |
| |
| #[test] |
| fn find_call() { |
| let states = vec![]; |
| let call = Call::find(states.into_iter()); |
| assert_eq!(call, Call::None); |
| |
| let states = vec![CallState::Terminated]; |
| let call = Call::find(states.into_iter()); |
| assert_eq!(call, Call::None); |
| |
| let states = vec![CallState::OngoingActive]; |
| let call = Call::find(states.into_iter()); |
| assert_eq!(call, Call::Some); |
| |
| let states = vec![CallState::OngoingHeld]; |
| let call = Call::find(states.into_iter()); |
| assert_eq!(call, Call::Some); |
| |
| let states = vec![CallState::OngoingHeld, CallState::Terminated]; |
| let call = Call::find(states.into_iter()); |
| assert_eq!(call, Call::Some); |
| |
| let states = vec![CallState::OngoingHeld, CallState::OngoingActive]; |
| let call = Call::find(states.into_iter()); |
| assert_eq!(call, Call::Some); |
| } |
| |
| #[test] |
| fn find_callsetup() { |
| let states = vec![]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::None); |
| |
| let states = vec![CallState::Terminated]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::None); |
| |
| let states = vec![CallState::IncomingRinging]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::Incoming); |
| |
| let states = vec![CallState::IncomingWaiting]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::Incoming); |
| |
| let states = vec![CallState::OutgoingAlerting]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::OutgoingAlerting); |
| |
| let states = vec![CallState::OutgoingDialing]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::OutgoingDialing); |
| |
| // The first setup state is used. |
| let states = vec![CallState::OutgoingDialing, CallState::IncomingRinging]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::OutgoingDialing); |
| |
| // Other states have no effect |
| let states = vec![CallState::Terminated, CallState::IncomingRinging]; |
| let setup = CallSetup::find(states.into_iter()); |
| assert_eq!(setup, CallSetup::Incoming); |
| } |
| |
| #[test] |
| fn find_call_held() { |
| let states = vec![]; |
| let held = CallHeld::find(states.into_iter()); |
| assert_eq!(held, CallHeld::None); |
| |
| let states = vec![CallState::OngoingHeld]; |
| let held = CallHeld::find(states.into_iter()); |
| assert_eq!(held, CallHeld::Held); |
| |
| // Active without Held is None. |
| let states = vec![CallState::OngoingActive]; |
| let held = CallHeld::find(states.into_iter()); |
| assert_eq!(held, CallHeld::None); |
| |
| // Other states have no effect. |
| let states = vec![CallState::OngoingHeld, CallState::Terminated]; |
| let held = CallHeld::find(states.into_iter()); |
| assert_eq!(held, CallHeld::Held); |
| |
| // Held then active produces expected result. |
| let states = vec![CallState::OngoingHeld, CallState::OngoingActive]; |
| let held = CallHeld::find(states.into_iter()); |
| assert_eq!(held, CallHeld::HeldAndActive); |
| |
| // And so does the reverse. |
| let states = vec![CallState::OngoingActive, CallState::OngoingHeld]; |
| let held = CallHeld::find(states.into_iter()); |
| assert_eq!(held, CallHeld::HeldAndActive); |
| } |
| |
| #[test] |
| fn find_call_indicators() { |
| let states = vec![]; |
| let ind = CallIndicators::find(states.into_iter()); |
| assert_eq!(ind, CallIndicators::default()); |
| |
| let states = vec![CallState::OngoingHeld, CallState::IncomingRinging]; |
| let ind = CallIndicators::find(states.into_iter()); |
| let expected = CallIndicators { |
| call: Call::Some, |
| callsetup: CallSetup::Incoming, |
| callheld: CallHeld::Held, |
| }; |
| assert_eq!(ind, expected); |
| } |
| |
| #[test] |
| fn call_indicators_differences() { |
| let a = CallIndicators::default(); |
| let b = CallIndicators { ..a }; |
| assert!(b.difference(a).is_empty()); |
| |
| let a = CallIndicators::default(); |
| let b = CallIndicators { call: Call::Some, ..a }; |
| let expected = |
| CallIndicatorsUpdates { call: Some(Call::Some), ..CallIndicatorsUpdates::default() }; |
| assert_eq!(b.difference(a), expected); |
| |
| let a = CallIndicators::default(); |
| let b = CallIndicators { call: Call::Some, callheld: CallHeld::Held, ..a }; |
| let expected = CallIndicatorsUpdates { |
| call: Some(Call::Some), |
| callheld: Some(CallHeld::Held), |
| ..CallIndicatorsUpdates::default() |
| }; |
| assert_eq!(b.difference(a), expected); |
| |
| let a = CallIndicators { call: Call::Some, ..CallIndicators::default() }; |
| let b = CallIndicators { callsetup: CallSetup::Incoming, ..a }; |
| let expected = CallIndicatorsUpdates { |
| callsetup: Some(CallSetup::Incoming), |
| ..CallIndicatorsUpdates::default() |
| }; |
| assert_eq!(b.difference(a), expected); |
| } |
| |
| #[test] |
| fn call_indicator_updates_is_empty() { |
| let mut updates = CallIndicatorsUpdates::default(); |
| assert!(updates.is_empty()); |
| |
| updates.call = Some(Call::Some); |
| assert!(!updates.is_empty()); |
| |
| let mut updates = CallIndicatorsUpdates::default(); |
| updates.callsetup = Some(CallSetup::Incoming); |
| assert!(!updates.is_empty()); |
| |
| let mut updates = CallIndicatorsUpdates::default(); |
| updates.callheld = Some(CallHeld::Held); |
| assert!(!updates.is_empty()); |
| } |
| |
| #[test] |
| fn call_indicator_updates_to_vec() { |
| let mut updates = CallIndicatorsUpdates::default(); |
| assert_eq!(updates.to_vec(), vec![]); |
| |
| let call = Call::Some; |
| updates.call = Some(call); |
| assert_eq!(updates.to_vec(), vec![AgIndicator::Call(call as u8)]); |
| |
| let callsetup = CallSetup::Incoming; |
| updates.callsetup = Some(callsetup); |
| let expected = vec![AgIndicator::Call(call as u8), AgIndicator::CallSetup(callsetup as u8)]; |
| assert_eq!(updates.to_vec(), expected); |
| |
| let callheld = CallHeld::Held; |
| updates.callheld = Some(callheld); |
| let expected = vec![ |
| AgIndicator::Call(call as u8), |
| AgIndicator::CallSetup(callsetup as u8), |
| AgIndicator::CallHeld(callheld as u8), |
| ]; |
| assert_eq!(updates.to_vec(), expected); |
| } |
| } |