blob: 3c2a576a515cd1b2f31eb7d88a5b793cc98e42bf [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 super::{
AgUpdate, InformationRequest, Procedure, ProcedureError, ProcedureMarker, ProcedureRequest,
};
use at_commands as at;
use crate::peer::service_level_connection::SlcState;
/// Represents the current state of the HF request to get the Subscriber Number Information
/// as defined in HFP v1.8, Section 4.31.
#[derive(Debug, PartialEq, Clone, Copy)]
enum State {
/// Initial state of the Procedure.
Start,
/// A request has been received from the HF to get the Subscriber Number Information.
GetRequest,
/// Terminal state of the procedure.
Terminated,
}
impl State {
/// Transition to the next state in the SubscriberNumberInformation procedure.
fn transition(&mut self) {
match *self {
Self::Start => *self = Self::GetRequest,
Self::GetRequest => *self = Self::Terminated,
Self::Terminated => *self = Self::Terminated,
}
}
}
/// The HF may retrieve the AG Subscriber Number Information via this
/// procedure. See HFP v1.8, Section 4.31.
///
/// 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.
#[derive(Debug)]
pub struct SubscriberNumberInformationProcedure {
/// The current state of the procedure
state: State,
}
impl Default for SubscriberNumberInformationProcedure {
fn default() -> Self {
Self { state: State::Start }
}
}
impl SubscriberNumberInformationProcedure {
/// Create a new SubscriberNumberInformation procedure in the Start state.
pub fn new() -> Self {
Self { state: State::Start }
}
}
impl Procedure for SubscriberNumberInformationProcedure {
fn marker(&self) -> ProcedureMarker {
ProcedureMarker::SubscriberNumberInformation
}
fn hf_update(&mut self, update: at::Command, _state: &mut SlcState) -> ProcedureRequest {
match (self.state, update) {
(State::Start, at::Command::Cnum {}) => {
self.state.transition();
let response = Box::new(|numbers| AgUpdate::SubscriberNumbers(numbers));
InformationRequest::GetSubscriberNumberInformation { response }.into()
}
(_, update) => ProcedureRequest::Error(ProcedureError::UnexpectedHf(update)),
}
}
fn ag_update(&mut self, update: AgUpdate, _state: &mut SlcState) -> ProcedureRequest {
match (self.state, update) {
(State::GetRequest, update @ AgUpdate::SubscriberNumbers(..)) => {
self.state.transition();
update.into()
}
(_, update) => ProcedureRequest::Error(ProcedureError::UnexpectedAg(update)),
}
}
fn is_terminated(&self) -> bool {
self.state == State::Terminated
}
}
/// Build an AT CNUM response from a number. See HFP v1.8, Section 4.34.2 for information on the
/// +CNUM fields.
pub fn build_cnum_response(number: String) -> at::Response {
at::success(at::Success::Cnum {
// Unsupported, shall be left blank.
alpha: "".into(),
number,
// Format: no changes to the number presentation required
ty: 128,
// Unsupported, shall be left blank.
speed: "".into(),
// Indicates voice service.
service: 4,
})
}
#[cfg(test)]
mod tests {
use super::*;
use matches::assert_matches;
#[test]
fn correct_marker() {
let marker = SubscriberNumberInformationProcedure::new().marker();
assert_eq!(marker, ProcedureMarker::SubscriberNumberInformation);
}
#[test]
fn procedure_handles_invalid_messages() {
let mut proc = SubscriberNumberInformationProcedure::new();
let req = proc.hf_update(at::Command::CindRead {}, &mut SlcState::default());
assert_matches!(req, ProcedureRequest::Error(ProcedureError::UnexpectedHf(_)));
let req = proc.ag_update(AgUpdate::ThreeWaySupport, &mut SlcState::default());
assert_matches!(req, ProcedureRequest::Error(ProcedureError::UnexpectedAg(_)));
}
#[test]
fn procedure_with_ok_response() {
let mut proc = SubscriberNumberInformationProcedure::new();
let req = proc.hf_update(at::Command::Cnum {}, &mut SlcState::default());
let update = match req {
ProcedureRequest::Info(InformationRequest::GetSubscriberNumberInformation {
response,
}) => response(vec![]),
x => panic!("Unexpected message: {:?}", x),
};
let req = proc.ag_update(update, &mut SlcState::default());
assert_matches!(
req,
ProcedureRequest::SendMessages(msgs) if msgs == vec![at::Response::Ok]
);
assert!(proc.is_terminated());
}
#[test]
fn procedure_with_numbers_response() {
let mut proc = SubscriberNumberInformationProcedure::new();
let req = proc.hf_update(at::Command::Cnum {}, &mut SlcState::default());
let number = String::from("1234567");
let update = match req {
ProcedureRequest::Info(InformationRequest::GetSubscriberNumberInformation {
response,
}) => response(vec![number.clone()]),
x => panic!("Unexpected message: {:?}", x),
};
let req = proc.ag_update(update, &mut SlcState::default());
let cnum_response = build_cnum_response(number);
assert_matches!(
req,
ProcedureRequest::SendMessages(msgs) if msgs == vec![cnum_response, at::Response::Ok]
);
assert!(proc.is_terminated());
}
}