• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::bindings::root as bindings;
2 use crate::btif::{BluetoothInterface, BtStatus, RawAddress, SupportedProfiles, ToggleableProfile};
3 use crate::ccall;
4 use crate::profiles::hf_client::bindings::bthf_client_interface_t;
5 use crate::topstack::get_dispatchers;
6 use crate::utils::{LTCheckedPtr, LTCheckedPtrMut};
7 
8 use num_derive::{FromPrimitive, ToPrimitive};
9 use num_traits::cast::FromPrimitive;
10 use std::sync::{Arc, Mutex};
11 use topshim_macros::{cb_variant, profile_enabled_or};
12 
13 use log::warn;
14 
15 #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)]
16 #[repr(u32)]
17 /// Represents the various connection states a Hands-Free client would go through.
18 pub enum BthfClientConnectionState {
19     Disconnected = 0,
20     Connecting,
21     Connected,
22     SlcConnected,
23     Disconnecting,
24 }
25 
26 #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)]
27 #[repr(u32)]
28 /// Represents the various connection states the audio channel for a
29 /// Hands-Free client would go through.
30 pub enum BthfClientAudioState {
31     Disconnected = 0,
32     Connecting,
33     Connected,
34     Disconnecting,
35 }
36 
37 impl From<bindings::bthf_client_connection_state_t> for BthfClientConnectionState {
from(item: bindings::bthf_client_connection_state_t) -> Self38     fn from(item: bindings::bthf_client_connection_state_t) -> Self {
39         BthfClientConnectionState::from_u32(item).unwrap_or(BthfClientConnectionState::Disconnected)
40     }
41 }
42 
43 impl From<bindings::bthf_client_audio_state_t> for BthfClientAudioState {
from(item: bindings::bthf_client_audio_state_t) -> Self44     fn from(item: bindings::bthf_client_audio_state_t) -> Self {
45         BthfClientAudioState::from_u32(item).unwrap_or(BthfClientAudioState::Disconnected)
46     }
47 }
48 
49 #[derive(Debug)]
50 pub enum BthfClientCallbacks {
51     /// Callback invoked when the connection state of the client changes.
52     /// Params (Address, Connection state, peer features, child features)
53     ConnectionState(RawAddress, BthfClientConnectionState, u32, u32),
54 
55     /// Callback invoked when the audio connection state of the client changes.
56     AudioState(RawAddress, BthfClientAudioState),
57     // TODO(b/262264556): Incomplete implementation. Other callbacks will be implemented if necessary.
58 }
59 
60 pub struct BthfClientCallbacksDispatcher {
61     pub dispatch: Box<dyn Fn(BthfClientCallbacks) + Send>,
62 }
63 
64 type BthfClientCb = Arc<Mutex<BthfClientCallbacksDispatcher>>;
65 
66 cb_variant!(
67     BthfClientCb,
68     hf_client_connection_state_cb -> BthfClientCallbacks::ConnectionState,
69     *const RawAddress,
70     bindings::bthf_client_connection_state_t -> BthfClientConnectionState,
71     u32, u32, {
72         let _0 = unsafe { *_0 };
73     }
74 );
75 
76 cb_variant!(
77     BthfClientCb,
78     hf_client_audio_state_cb -> BthfClientCallbacks::AudioState,
79     *const RawAddress,
80     bindings::bthf_client_audio_state_t -> BthfClientAudioState,{
81         let _0 = unsafe { *_0 };
82     }
83 );
84 
85 struct RawHfClientWrapper {
86     raw: *const bindings::bthf_client_interface_t,
87 }
88 
89 unsafe impl Send for RawHfClientWrapper {}
90 
91 pub struct HfClient {
92     internal: RawHfClientWrapper,
93     is_init: bool,
94     is_enabled: bool,
95     callbacks: Option<Box<bindings::bthf_client_callbacks_t>>,
96 }
97 
98 impl ToggleableProfile for HfClient {
is_enabled(&self) -> bool99     fn is_enabled(&self) -> bool {
100         self.is_enabled
101     }
102 
enable(&mut self) -> bool103     fn enable(&mut self) -> bool {
104         let cb_ptr = LTCheckedPtrMut::from(self.callbacks.as_mut().unwrap());
105 
106         let init = ccall!(self, init, cb_ptr.into());
107         self.is_init = BtStatus::from(init) == BtStatus::Success;
108         self.is_enabled = self.is_init;
109         true
110     }
111 
112     #[profile_enabled_or(false)]
disable(&mut self) -> bool113     fn disable(&mut self) -> bool {
114         ccall!(self, cleanup);
115         self.is_enabled = false;
116         true
117     }
118 }
119 
120 impl HfClient {
new(intf: &BluetoothInterface) -> HfClient121     pub fn new(intf: &BluetoothInterface) -> HfClient {
122         let r = intf.get_profile_interface(SupportedProfiles::HfClient);
123         HfClient {
124             internal: RawHfClientWrapper { raw: r as *const bthf_client_interface_t },
125             is_init: false,
126             is_enabled: false,
127             callbacks: None,
128         }
129     }
130 
is_initialized(&self) -> bool131     pub fn is_initialized(&self) -> bool {
132         self.is_init
133     }
134 
initialize(&mut self, callbacks: BthfClientCallbacksDispatcher) -> bool135     pub fn initialize(&mut self, callbacks: BthfClientCallbacksDispatcher) -> bool {
136         // Register dispatcher
137         if get_dispatchers().lock().unwrap().set::<BthfClientCb>(Arc::new(Mutex::new(callbacks))) {
138             panic!("Tried to set dispatcher for BthfClienCallbacks but it already existed");
139         }
140 
141         let callbacks = Box::new(bindings::bthf_client_callbacks_t {
142             // TODO(b/262264556): Incomplete implementation. Only necessary callbacks are implemented currently.
143             size: 22 * 8,
144             connection_state_cb: Some(hf_client_connection_state_cb),
145             audio_state_cb: Some(hf_client_audio_state_cb),
146             vr_cmd_cb: None,
147             network_state_cb: None,
148             network_roaming_cb: None,
149             network_signal_cb: None,
150             battery_level_cb: None,
151             current_operator_cb: None,
152             call_cb: None,
153             callsetup_cb: None,
154             callheld_cb: None,
155             resp_and_hold_cb: None,
156             clip_cb: None,
157             call_waiting_cb: None,
158             current_calls_cb: None,
159             volume_change_cb: None,
160             cmd_complete_cb: None,
161             subscriber_info_cb: None,
162             in_band_ring_tone_cb: None,
163             last_voice_tag_number_callback: None,
164             ring_indication_cb: None,
165             unknown_event_cb: None,
166         });
167         self.callbacks = Some(callbacks);
168 
169         true
170     }
171 
172     #[profile_enabled_or(BtStatus::NotReady)]
connect(&self, addr: RawAddress) -> BtStatus173     pub fn connect(&self, addr: RawAddress) -> BtStatus {
174         let addr_ptr = LTCheckedPtr::from_ref(&addr);
175         BtStatus::from(ccall!(self, connect, addr_ptr.into()))
176     }
177 
178     #[profile_enabled_or(BtStatus::NotReady)]
disconnect(&self, addr: RawAddress) -> BtStatus179     pub fn disconnect(&self, addr: RawAddress) -> BtStatus {
180         let addr_ptr = LTCheckedPtr::from_ref(&addr);
181         BtStatus::from(ccall!(self, disconnect, addr_ptr.into()))
182     }
183 
184     #[profile_enabled_or(BtStatus::NotReady)]
connect_audio(&mut self, addr: RawAddress) -> BtStatus185     pub fn connect_audio(&mut self, addr: RawAddress) -> BtStatus {
186         let addr_ptr = LTCheckedPtr::from_ref(&addr);
187         BtStatus::from(ccall!(self, connect_audio, addr_ptr.into()))
188     }
189 
190     #[profile_enabled_or(BtStatus::NotReady)]
disconnect_audio(&mut self, addr: RawAddress) -> BtStatus191     pub fn disconnect_audio(&mut self, addr: RawAddress) -> BtStatus {
192         let addr_ptr = LTCheckedPtr::from_ref(&addr);
193         BtStatus::from(ccall!(self, disconnect_audio, addr_ptr.into()))
194     }
195 
196     #[profile_enabled_or]
cleanup(&mut self)197     pub fn cleanup(&mut self) {
198         ccall!(self, cleanup)
199     }
200     // TODO(b/262264556): Incomplete API implementation. Only necessary APIs are implemented currently.
201 }
202