bt_hfp/call/
indicators.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_bluetooth_hfp::CallState;
6use packet_encoding::decodable_enum;
7use thiserror::Error;
8
9decodable_enum! {
10/// The Call Indicator as specified in HFP v1.8, Section 4.10.1
11    pub enum Call<i64, CallIndicatorError, InvalidValue> {
12        /// There are no calls present in the AG (active or held).
13        None = 0,
14        /// There is at least one call present in the AG (active or held).
15        Some = 1,
16    }
17}
18
19#[derive(Debug, Error, PartialEq)]
20pub enum CallIndicatorError {
21    #[error("Invalid Call indicator value")]
22    InvalidValue,
23}
24
25impl Default for Call {
26    fn default() -> Self {
27        Self::None
28    }
29}
30
31impl From<&Call> for bool {
32    fn from(call: &Call) -> Self {
33        match call {
34            Call::None => false,
35            Call::Some => true,
36        }
37    }
38}
39
40impl From<bool> for Call {
41    fn from(call: bool) -> Self {
42        match call {
43            false => Self::None,
44            true => Self::Some,
45        }
46    }
47}
48
49impl Call {
50    /// Find the Call state based on all the calls in `iter`.
51    pub fn find(mut iter: impl Iterator<Item = CallState>) -> Self {
52        iter.any(|state| {
53            [CallState::OngoingActive, CallState::OngoingHeld, CallState::TransferredToAg]
54                .contains(&state)
55        })
56        .into()
57    }
58}
59
60decodable_enum! {
61/// The Callsetup Indicator as specified in HFP v1.8, Section 4.10.2
62    pub enum CallSetup<i64, CallSetupIndicatorError, InvalidValue> {
63        /// No call setup in progress.
64        None = 0,
65        /// Incoming call setup in progress.
66        Incoming = 1,
67        /// Outgoing call setup in dialing state.
68        OutgoingDialing = 2,
69        /// Outgoing call setup in alerting state.
70        OutgoingAlerting = 3,
71    }
72}
73
74#[derive(Debug, Error, PartialEq)]
75pub enum CallSetupIndicatorError {
76    #[error("Invalid Call Setup indicator value.")]
77    InvalidValue,
78}
79
80impl Default for CallSetup {
81    fn default() -> Self {
82        Self::None
83    }
84}
85
86impl CallSetup {
87    /// Find CallSetup state based on the first call in `iter` that is in a callsetup state.
88    pub fn find(mut iter: impl Iterator<Item = CallState>) -> Self {
89        iter.find(|state| {
90            [
91                CallState::IncomingRinging,
92                CallState::IncomingWaiting,
93                CallState::OutgoingAlerting,
94                CallState::OutgoingDialing,
95            ]
96            .contains(&state)
97        })
98        .map(CallSetup::from)
99        .unwrap_or(CallSetup::None)
100    }
101}
102
103impl From<CallState> for CallSetup {
104    fn from(state: CallState) -> Self {
105        match state {
106            CallState::IncomingRinging | CallState::IncomingWaiting => Self::Incoming,
107            CallState::OutgoingDialing => Self::OutgoingDialing,
108            CallState::OutgoingAlerting => Self::OutgoingAlerting,
109            _ => Self::None,
110        }
111    }
112}
113
114decodable_enum! {
115    /// The Callheld Indicator as specified in HFP v1.8, Section 4.10.3
116    pub enum CallHeld<i64, CallHeldIndicatorError, InvalidValue> {
117        /// No calls held.
118        None = 0,
119        /// Call is placed on hold or active/held calls swapped (The AG has both an active AND a held
120        /// call).
121        HeldAndActive = 1,
122        /// Call on hold, no active call.
123        Held = 2,
124    }
125}
126
127#[derive(Debug, Error, PartialEq)]
128pub enum CallHeldIndicatorError {
129    #[error("Invalid Call Held indicator value")]
130    InvalidValue,
131}
132
133impl Default for CallHeld {
134    fn default() -> Self {
135        Self::None
136    }
137}
138
139impl CallHeld {
140    /// Find the CallHeld state based on all calls in `iter`.
141    pub fn find(mut iter: impl Iterator<Item = CallState> + Clone) -> Self {
142        let any_held = iter.clone().any(|state| state == CallState::OngoingHeld);
143        let any_active = iter.any(|state| state == CallState::OngoingActive);
144        match (any_held, any_active) {
145            (true, false) => CallHeld::Held,
146            (true, true) => CallHeld::HeldAndActive,
147            (false, _) => CallHeld::None,
148        }
149    }
150}
151
152#[derive(Clone, Copy, Debug, Default, PartialEq)]
153pub struct CallIndicators {
154    pub call: Call,
155    pub callsetup: CallSetup,
156    pub callheld: CallHeld,
157    /// There is at least one call in the IncomingWaiting state. `callwaiting` is distinct from
158    /// other fields in that it doesn't map to a specific CIEV phone status indicator.
159    pub callwaiting: bool,
160}
161
162impl CallIndicators {
163    /// Find CallIndicators based on all items in `iter`.
164    pub fn find(mut iter: impl Iterator<Item = CallState> + Clone) -> Self {
165        let call = Call::find(iter.clone());
166        let callsetup = CallSetup::find(iter.clone());
167        let callheld = CallHeld::find(iter.clone());
168        let callwaiting = iter.any(|c| c == CallState::IncomingWaiting);
169        CallIndicators { call, callsetup, callheld, callwaiting }
170    }
171
172    /// A list of all the statuses that have changed between `other` and self.
173    /// The values in the list are the values found in `self`.
174    pub fn difference(&self, other: Self) -> CallIndicatorsUpdates {
175        let mut changes = CallIndicatorsUpdates::default();
176        if other.call != self.call {
177            changes.call = Some(self.call);
178        }
179        if other.callsetup != self.callsetup {
180            changes.callsetup = Some(self.callsetup);
181        }
182        if other.callheld != self.callheld {
183            changes.callheld = Some(self.callheld);
184        }
185        if self.callwaiting && !other.callwaiting {
186            changes.callwaiting = true;
187        }
188        changes
189    }
190}
191
192#[derive(Clone, Copy, Debug, Default, PartialEq)]
193pub struct CallIndicatorsUpdates {
194    pub call: Option<Call>,
195    pub callsetup: Option<CallSetup>,
196    pub callheld: Option<CallHeld>,
197    /// Indicates whether there is a call that has changed to the CallWaiting state in this update.
198    pub callwaiting: bool,
199}
200
201impl CallIndicatorsUpdates {
202    /// Returns true if all fields are `None` or false.
203    pub fn is_empty(&self) -> bool {
204        self.call.is_none()
205            && self.callsetup.is_none()
206            && self.callheld.is_none()
207            && !self.callwaiting
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[fuchsia::test]
216    fn find_call() {
217        let states = vec![];
218        let call = Call::find(states.into_iter());
219        assert_eq!(call, Call::None);
220
221        let states = vec![CallState::Terminated];
222        let call = Call::find(states.into_iter());
223        assert_eq!(call, Call::None);
224
225        let states = vec![CallState::OngoingActive];
226        let call = Call::find(states.into_iter());
227        assert_eq!(call, Call::Some);
228
229        let states = vec![CallState::OngoingHeld];
230        let call = Call::find(states.into_iter());
231        assert_eq!(call, Call::Some);
232
233        let states = vec![CallState::OngoingHeld, CallState::Terminated];
234        let call = Call::find(states.into_iter());
235        assert_eq!(call, Call::Some);
236
237        let states = vec![CallState::OngoingHeld, CallState::OngoingActive];
238        let call = Call::find(states.into_iter());
239        assert_eq!(call, Call::Some);
240    }
241
242    #[fuchsia::test]
243    fn find_callsetup() {
244        let states = vec![];
245        let setup = CallSetup::find(states.into_iter());
246        assert_eq!(setup, CallSetup::None);
247
248        let states = vec![CallState::Terminated];
249        let setup = CallSetup::find(states.into_iter());
250        assert_eq!(setup, CallSetup::None);
251
252        let states = vec![CallState::IncomingRinging];
253        let setup = CallSetup::find(states.into_iter());
254        assert_eq!(setup, CallSetup::Incoming);
255
256        let states = vec![CallState::IncomingWaiting];
257        let setup = CallSetup::find(states.into_iter());
258        assert_eq!(setup, CallSetup::Incoming);
259
260        let states = vec![CallState::OutgoingAlerting];
261        let setup = CallSetup::find(states.into_iter());
262        assert_eq!(setup, CallSetup::OutgoingAlerting);
263
264        let states = vec![CallState::OutgoingDialing];
265        let setup = CallSetup::find(states.into_iter());
266        assert_eq!(setup, CallSetup::OutgoingDialing);
267
268        // The first setup state is used.
269        let states = vec![CallState::OutgoingDialing, CallState::IncomingRinging];
270        let setup = CallSetup::find(states.into_iter());
271        assert_eq!(setup, CallSetup::OutgoingDialing);
272
273        // Other states have no effect
274        let states = vec![CallState::Terminated, CallState::IncomingRinging];
275        let setup = CallSetup::find(states.into_iter());
276        assert_eq!(setup, CallSetup::Incoming);
277    }
278
279    #[fuchsia::test]
280    fn find_call_held() {
281        let states = vec![];
282        let held = CallHeld::find(states.into_iter());
283        assert_eq!(held, CallHeld::None);
284
285        let states = vec![CallState::OngoingHeld];
286        let held = CallHeld::find(states.into_iter());
287        assert_eq!(held, CallHeld::Held);
288
289        // Active without Held is None.
290        let states = vec![CallState::OngoingActive];
291        let held = CallHeld::find(states.into_iter());
292        assert_eq!(held, CallHeld::None);
293
294        // Other states have no effect.
295        let states = vec![CallState::OngoingHeld, CallState::Terminated];
296        let held = CallHeld::find(states.into_iter());
297        assert_eq!(held, CallHeld::Held);
298
299        // Held then active produces expected result.
300        let states = vec![CallState::OngoingHeld, CallState::OngoingActive];
301        let held = CallHeld::find(states.into_iter());
302        assert_eq!(held, CallHeld::HeldAndActive);
303
304        // And so does the reverse.
305        let states = vec![CallState::OngoingActive, CallState::OngoingHeld];
306        let held = CallHeld::find(states.into_iter());
307        assert_eq!(held, CallHeld::HeldAndActive);
308    }
309
310    #[fuchsia::test]
311    fn find_call_indicators() {
312        let states = vec![];
313        let ind = CallIndicators::find(states.into_iter());
314        assert_eq!(ind, CallIndicators::default());
315
316        let states = vec![CallState::OngoingHeld, CallState::IncomingRinging];
317        let ind = CallIndicators::find(states.into_iter());
318        let expected = CallIndicators {
319            call: Call::Some,
320            callsetup: CallSetup::Incoming,
321            callwaiting: false,
322            callheld: CallHeld::Held,
323        };
324        assert_eq!(ind, expected);
325    }
326
327    #[fuchsia::test]
328    fn call_indicators_differences() {
329        let a = CallIndicators::default();
330        let b = CallIndicators { ..a };
331        assert!(b.difference(a).is_empty());
332
333        let a = CallIndicators::default();
334        let b = CallIndicators { call: Call::Some, ..a };
335        let expected =
336            CallIndicatorsUpdates { call: Some(Call::Some), ..CallIndicatorsUpdates::default() };
337        assert_eq!(b.difference(a), expected);
338
339        let a = CallIndicators::default();
340        let b = CallIndicators { call: Call::Some, callheld: CallHeld::Held, ..a };
341        let expected = CallIndicatorsUpdates {
342            call: Some(Call::Some),
343            callheld: Some(CallHeld::Held),
344            ..CallIndicatorsUpdates::default()
345        };
346        assert_eq!(b.difference(a), expected);
347
348        let a = CallIndicators { call: Call::Some, ..CallIndicators::default() };
349        let b = CallIndicators { callsetup: CallSetup::Incoming, ..a };
350        let expected = CallIndicatorsUpdates {
351            callsetup: Some(CallSetup::Incoming),
352            ..CallIndicatorsUpdates::default()
353        };
354        assert_eq!(b.difference(a), expected);
355
356        let a = CallIndicators::default();
357        let b = CallIndicators { callsetup: CallSetup::Incoming, callwaiting: true, ..a };
358        let expected = CallIndicatorsUpdates {
359            callsetup: Some(CallSetup::Incoming),
360            callwaiting: true,
361            ..CallIndicatorsUpdates::default()
362        };
363        assert_eq!(b.difference(a), expected);
364
365        // reverse: going from b to a.
366        let expected = CallIndicatorsUpdates {
367            callsetup: Some(CallSetup::None),
368            callwaiting: false,
369            ..CallIndicatorsUpdates::default()
370        };
371        assert_eq!(a.difference(b), expected);
372    }
373
374    #[fuchsia::test]
375    fn call_indicator_updates_is_empty() {
376        let mut updates = CallIndicatorsUpdates::default();
377        assert!(updates.is_empty());
378
379        updates.call = Some(Call::Some);
380        assert!(!updates.is_empty());
381
382        let mut updates = CallIndicatorsUpdates::default();
383        updates.callsetup = Some(CallSetup::Incoming);
384        assert!(!updates.is_empty());
385
386        let mut updates = CallIndicatorsUpdates::default();
387        updates.callwaiting = true;
388        assert!(!updates.is_empty());
389
390        let mut updates = CallIndicatorsUpdates::default();
391        updates.callheld = Some(CallHeld::Held);
392        assert!(!updates.is_empty());
393    }
394}