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::fmt::{Debug, Formatter, Result}; 11 use std::sync::{Arc, Mutex}; 12 use topshim_macros::{cb_variant, log_args, profile_enabled_or}; 13 14 use log::warn; 15 16 #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] 17 #[repr(u32)] 18 /// Represents the various connection states a Hands-Free client would go through. 19 pub enum BthfClientConnectionState { 20 Disconnected = 0, 21 Connecting, 22 Connected, 23 SlcConnected, 24 Disconnecting, 25 } 26 27 #[derive(Clone, Debug, FromPrimitive, ToPrimitive, PartialEq, PartialOrd)] 28 #[repr(u32)] 29 /// Represents the various connection states the audio channel for a 30 /// Hands-Free client would go through. 31 pub enum BthfClientAudioState { 32 Disconnected = 0, 33 Connecting, 34 Connected, 35 Disconnecting, 36 } 37 38 impl From<bindings::bthf_client_connection_state_t> for BthfClientConnectionState { from(item: bindings::bthf_client_connection_state_t) -> Self39 fn from(item: bindings::bthf_client_connection_state_t) -> Self { 40 BthfClientConnectionState::from_u32(item).unwrap_or(BthfClientConnectionState::Disconnected) 41 } 42 } 43 44 impl From<bindings::bthf_client_audio_state_t> for BthfClientAudioState { from(item: bindings::bthf_client_audio_state_t) -> Self45 fn from(item: bindings::bthf_client_audio_state_t) -> Self { 46 BthfClientAudioState::from_u32(item).unwrap_or(BthfClientAudioState::Disconnected) 47 } 48 } 49 50 #[derive(Debug)] 51 pub enum BthfClientCallbacks { 52 /// Callback invoked when the connection state of the client changes. 53 /// Params (Address, Connection state, peer features, child features) 54 ConnectionState(RawAddress, BthfClientConnectionState, u32, u32), 55 56 /// Callback invoked when the audio connection state of the client changes. 57 AudioState(RawAddress, BthfClientAudioState), 58 // TODO(b/262264556): Incomplete implementation. Other callbacks will be implemented if necessary. 59 } 60 61 pub struct BthfClientCallbacksDispatcher { 62 pub dispatch: Box<dyn Fn(BthfClientCallbacks) + Send>, 63 } 64 65 impl Debug for BthfClientCallbacksDispatcher { fmt(&self, f: &mut Formatter<'_>) -> Result66 fn fmt(&self, f: &mut Formatter<'_>) -> Result { 67 write!(f, "BthfClientCallbacksDispatcher {{}}") 68 } 69 } 70 71 type BthfClientCb = Arc<Mutex<BthfClientCallbacksDispatcher>>; 72 73 cb_variant!( 74 BthfClientCb, 75 hf_client_connection_state_cb -> BthfClientCallbacks::ConnectionState, 76 *const RawAddress, 77 bindings::bthf_client_connection_state_t -> BthfClientConnectionState, 78 u32, u32, { 79 let _0 = unsafe { *_0 }; 80 } 81 ); 82 83 cb_variant!( 84 BthfClientCb, 85 hf_client_audio_state_cb -> BthfClientCallbacks::AudioState, 86 *const RawAddress, 87 bindings::bthf_client_audio_state_t -> BthfClientAudioState,{ 88 let _0 = unsafe { *_0 }; 89 } 90 ); 91 92 struct RawHfClientWrapper { 93 raw: *const bindings::bthf_client_interface_t, 94 } 95 96 unsafe impl Send for RawHfClientWrapper {} 97 98 pub struct HfClient { 99 internal: RawHfClientWrapper, 100 is_init: bool, 101 is_enabled: bool, 102 callbacks: Option<Box<bindings::bthf_client_callbacks_t>>, 103 } 104 105 impl ToggleableProfile for HfClient { is_enabled(&self) -> bool106 fn is_enabled(&self) -> bool { 107 self.is_enabled 108 } 109 enable(&mut self) -> bool110 fn enable(&mut self) -> bool { 111 let cb_ptr = LTCheckedPtrMut::from(self.callbacks.as_mut().unwrap()); 112 113 let init = ccall!(self, init, cb_ptr.into()); 114 self.is_init = BtStatus::from(init) == BtStatus::Success; 115 self.is_enabled = self.is_init; 116 true 117 } 118 119 #[profile_enabled_or(false)] disable(&mut self) -> bool120 fn disable(&mut self) -> bool { 121 ccall!(self, cleanup); 122 self.is_enabled = false; 123 true 124 } 125 } 126 127 impl HfClient { 128 #[log_args] new(intf: &BluetoothInterface) -> HfClient129 pub fn new(intf: &BluetoothInterface) -> HfClient { 130 let r = intf.get_profile_interface(SupportedProfiles::HfClient); 131 HfClient { 132 internal: RawHfClientWrapper { raw: r as *const bthf_client_interface_t }, 133 is_init: false, 134 is_enabled: false, 135 callbacks: None, 136 } 137 } 138 139 #[log_args] is_initialized(&self) -> bool140 pub fn is_initialized(&self) -> bool { 141 self.is_init 142 } 143 144 #[log_args] initialize(&mut self, callbacks: BthfClientCallbacksDispatcher) -> bool145 pub fn initialize(&mut self, callbacks: BthfClientCallbacksDispatcher) -> bool { 146 // Register dispatcher 147 if get_dispatchers().lock().unwrap().set::<BthfClientCb>(Arc::new(Mutex::new(callbacks))) { 148 panic!("Tried to set dispatcher for BthfClienCallbacks but it already existed"); 149 } 150 151 let callbacks = Box::new(bindings::bthf_client_callbacks_t { 152 // TODO(b/262264556): Incomplete implementation. Only necessary callbacks are implemented currently. 153 size: 22 * 8, 154 connection_state_cb: Some(hf_client_connection_state_cb), 155 audio_state_cb: Some(hf_client_audio_state_cb), 156 vr_cmd_cb: None, 157 network_state_cb: None, 158 network_roaming_cb: None, 159 network_signal_cb: None, 160 battery_level_cb: None, 161 current_operator_cb: None, 162 call_cb: None, 163 callsetup_cb: None, 164 callheld_cb: None, 165 resp_and_hold_cb: None, 166 clip_cb: None, 167 call_waiting_cb: None, 168 current_calls_cb: None, 169 volume_change_cb: None, 170 cmd_complete_cb: None, 171 subscriber_info_cb: None, 172 in_band_ring_tone_cb: None, 173 last_voice_tag_number_callback: None, 174 ring_indication_cb: None, 175 unknown_event_cb: None, 176 }); 177 self.callbacks = Some(callbacks); 178 179 true 180 } 181 182 #[log_args] 183 #[profile_enabled_or(BtStatus::NotReady)] connect(&self, addr: RawAddress) -> BtStatus184 pub fn connect(&self, addr: RawAddress) -> BtStatus { 185 let addr_ptr = LTCheckedPtr::from_ref(&addr); 186 BtStatus::from(ccall!(self, connect, addr_ptr.into())) 187 } 188 189 #[log_args] 190 #[profile_enabled_or(BtStatus::NotReady)] disconnect(&self, addr: RawAddress) -> BtStatus191 pub fn disconnect(&self, addr: RawAddress) -> BtStatus { 192 let addr_ptr = LTCheckedPtr::from_ref(&addr); 193 BtStatus::from(ccall!(self, disconnect, addr_ptr.into())) 194 } 195 196 #[log_args] 197 #[profile_enabled_or(BtStatus::NotReady)] connect_audio(&mut self, addr: RawAddress) -> BtStatus198 pub fn connect_audio(&mut self, addr: RawAddress) -> BtStatus { 199 let addr_ptr = LTCheckedPtr::from_ref(&addr); 200 BtStatus::from(ccall!(self, connect_audio, addr_ptr.into())) 201 } 202 203 #[log_args] 204 #[profile_enabled_or(BtStatus::NotReady)] disconnect_audio(&mut self, addr: RawAddress) -> BtStatus205 pub fn disconnect_audio(&mut self, addr: RawAddress) -> BtStatus { 206 let addr_ptr = LTCheckedPtr::from_ref(&addr); 207 BtStatus::from(ccall!(self, disconnect_audio, addr_ptr.into())) 208 } 209 210 #[log_args] 211 #[profile_enabled_or] cleanup(&mut self)212 pub fn cleanup(&mut self) { 213 ccall!(self, cleanup) 214 } 215 // TODO(b/262264556): Incomplete API implementation. Only necessary APIs are implemented currently. 216 } 217