blob: 582baba7da8e9e82037516b0d5d0374b564e528d [file] [log] [blame]
// 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;
use super::{AgUpdate, Procedure, ProcedureError, ProcedureMarker, ProcedureRequest, RING_BYTES};
use crate::peer::service_level_connection::SlcState;
/// Represents the Ring (Alert) procedures as defined in HFP v1.8 Section 4.13 - 4.14.
///
/// The AG may send an unsolicited RING to the HF via this procedure.
///
/// This procedure is implemented from the perspective of the AG. Namely, outgoing `requests`
/// typically request information about the current state of the AG, to be sent to the remote
/// peer acting as the HF.
pub struct RingProcedure {
// Whether the procedure has sent the Ring to the HF.
responded: bool,
}
impl RingProcedure {
pub fn new() -> Self {
Self { responded: false }
}
}
impl Procedure for RingProcedure {
fn marker(&self) -> ProcedureMarker {
ProcedureMarker::Ring
}
fn ag_update(&mut self, update: AgUpdate, state: &mut SlcState) -> ProcedureRequest {
match update {
update @ AgUpdate::Ring(..) if !self.responded => {
self.responded = true;
// Only send the CLIP if Call Line Identification Notifications are enabled for
// the SLC.
if state.call_line_ident_notifications {
update.into()
} else {
vec![at::Response::RawBytes(RING_BYTES.to_vec())].into()
}
}
_ => ProcedureRequest::Error(ProcedureError::UnexpectedAg(update)),
}
}
/// Returns true if the Procedure is finished.
fn is_terminated(&self) -> bool {
self.responded
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::peer::calls::{Call, Direction};
use {fidl_fuchsia_bluetooth_hfp::CallState, matches::assert_matches};
#[test]
fn correct_marker() {
let marker = RingProcedure::new().marker();
assert_eq!(marker, ProcedureMarker::Ring);
}
#[test]
fn unexpected_hf_update_returns_error() {
let mut procedure = RingProcedure::new();
let mut state = SlcState::default();
// SLCI AT command.
let random_hf = at::Command::CindRead {};
assert_matches!(
procedure.hf_update(random_hf, &mut state),
ProcedureRequest::Error(ProcedureError::UnexpectedHf(_))
);
}
#[test]
fn unexpected_ag_update_returns_error() {
let mut procedure = RingProcedure::new();
let mut state = SlcState::default();
// SLCI AT command.
let random_ag = AgUpdate::ThreeWaySupport;
assert_matches!(
procedure.ag_update(random_ag, &mut state),
ProcedureRequest::Error(ProcedureError::UnexpectedAg(_))
);
}
#[test]
fn update_with_clip_enabled_produces_expected_request() {
let mut procedure = RingProcedure::new();
let mut state = SlcState { call_line_ident_notifications: true, ..SlcState::default() };
assert!(!procedure.is_terminated());
let call =
Call::new(1, "123".into(), CallState::IncomingRinging, Direction::MobileTerminated);
let update = AgUpdate::Ring(call.clone());
let expected_messages = vec![
at::Response::RawBytes(RING_BYTES.to_vec()),
at::success(at::Success::Clip { ty: call.number.type_(), number: call.number.into() }),
];
assert_matches!(
procedure.ag_update(update, &mut state),
ProcedureRequest::SendMessages(m) if m == expected_messages
);
assert!(procedure.is_terminated());
}
#[test]
fn update_with_clip_disabled_produces_expected_request() {
let mut procedure = RingProcedure::new();
let mut state = SlcState { call_line_ident_notifications: false, ..SlcState::default() };
assert!(!procedure.is_terminated());
let call =
Call::new(1, "123".into(), CallState::IncomingRinging, Direction::MobileTerminated);
let update = AgUpdate::Ring(call.clone());
let expected_messages = vec![at::Response::RawBytes(RING_BYTES.to_vec())];
assert_matches!(
procedure.ag_update(update, &mut state),
ProcedureRequest::SendMessages(m) if m == expected_messages
);
assert!(procedure.is_terminated());
}
}