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