1 use crate::btif::{BluetoothInterface, RawAddress, ToggleableProfile};
2 use crate::topstack::get_dispatchers;
3 
4 use std::sync::{Arc, Mutex};
5 use topshim_macros::{cb_variant, profile_enabled_or};
6 
7 use log::warn;
8 
9 #[cxx::bridge(namespace = bluetooth::topshim::rust)]
10 pub mod ffi {
11     unsafe extern "C++" {
12         include!("types/raw_address.h");
13         #[namespace = ""]
14         type RawAddress = crate::btif::RawAddress;
15     }
16 
17     #[derive(Debug, Copy, Clone)]
18     pub enum BtVcConnectionState {
19         Disconnected = 0,
20         Connecting,
21         Connected,
22         Disconnecting,
23     }
24 
25     unsafe extern "C++" {
26         include!("vc/vc_shim.h");
27 
28         type VolumeControlIntf;
29 
GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>30         unsafe fn GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>;
31 
init(self: Pin<&mut VolumeControlIntf>)32         fn init(self: Pin<&mut VolumeControlIntf>);
cleanup(self: Pin<&mut VolumeControlIntf>)33         fn cleanup(self: Pin<&mut VolumeControlIntf>);
connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)34         fn connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)35         fn disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)36         fn remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8)37         fn set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8);
mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)38         fn mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress)39         fn unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress);
get_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, )40         fn get_ext_audio_out_volume_offset(
41             self: Pin<&mut VolumeControlIntf>,
42             addr: RawAddress,
43             ext_output_id: u8,
44         );
set_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, offset_val: i16, )45         fn set_ext_audio_out_volume_offset(
46             self: Pin<&mut VolumeControlIntf>,
47             addr: RawAddress,
48             ext_output_id: u8,
49             offset_val: i16,
50         );
get_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, )51         fn get_ext_audio_out_location(
52             self: Pin<&mut VolumeControlIntf>,
53             addr: RawAddress,
54             ext_output_id: u8,
55         );
set_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, location: u32, )56         fn set_ext_audio_out_location(
57             self: Pin<&mut VolumeControlIntf>,
58             addr: RawAddress,
59             ext_output_id: u8,
60             location: u32,
61         );
get_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, )62         fn get_ext_audio_out_description(
63             self: Pin<&mut VolumeControlIntf>,
64             addr: RawAddress,
65             ext_output_id: u8,
66         );
set_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, descr: *const c_char, )67         unsafe fn set_ext_audio_out_description(
68             self: Pin<&mut VolumeControlIntf>,
69             addr: RawAddress,
70             ext_output_id: u8,
71             descr: *const c_char,
72         );
73     }
74 
75     extern "Rust" {
vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress)76         fn vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress);
vc_volume_state_callback( address: RawAddress, volume: u8, mute: bool, is_autonomous: bool, )77         fn vc_volume_state_callback(
78             address: RawAddress,
79             volume: u8,
80             mute: bool,
81             is_autonomous: bool,
82         );
vc_group_volume_state_callback( group_id: i32, volume: u8, mute: bool, is_autonomous: bool, )83         fn vc_group_volume_state_callback(
84             group_id: i32,
85             volume: u8,
86             mute: bool,
87             is_autonomous: bool,
88         );
vc_device_available_callback(address: RawAddress, num_offset: u8)89         fn vc_device_available_callback(address: RawAddress, num_offset: u8);
vc_ext_audio_out_volume_offset_callback( address: RawAddress, ext_output_id: u8, offset: i16, )90         fn vc_ext_audio_out_volume_offset_callback(
91             address: RawAddress,
92             ext_output_id: u8,
93             offset: i16,
94         );
vc_ext_audio_out_location_callback( address: RawAddress, ext_output_id: u8, location: u32, )95         fn vc_ext_audio_out_location_callback(
96             address: RawAddress,
97             ext_output_id: u8,
98             location: u32,
99         );
vc_ext_audio_out_description_callback( address: RawAddress, ext_output_id: u8, descr: String, )100         fn vc_ext_audio_out_description_callback(
101             address: RawAddress,
102             ext_output_id: u8,
103             descr: String,
104         );
105     }
106 }
107 
108 pub type BtVcConnectionState = ffi::BtVcConnectionState;
109 
110 #[derive(Debug)]
111 pub enum VolumeControlCallbacks {
112     ConnectionState(BtVcConnectionState, RawAddress),
113     VolumeState(RawAddress, u8, bool, bool),
114     GroupVolumeState(i32, u8, bool, bool),
115     DeviceAvailable(RawAddress, u8),
116     ExtAudioOutVolume(RawAddress, u8, i16),
117     ExtAudioOutLocation(RawAddress, u8, u32),
118     ExtAudioOutDescription(RawAddress, u8, String),
119 }
120 
121 pub struct VolumeControlCallbacksDispatcher {
122     pub dispatch: Box<dyn Fn(VolumeControlCallbacks) + Send>,
123 }
124 
125 type VolumeControlCb = Arc<Mutex<VolumeControlCallbacksDispatcher>>;
126 
127 cb_variant!(VolumeControlCb,
128             vc_connection_state_callback -> VolumeControlCallbacks::ConnectionState,
129             BtVcConnectionState, RawAddress);
130 
131 cb_variant!(VolumeControlCb,
132             vc_volume_state_callback -> VolumeControlCallbacks::VolumeState,
133             RawAddress, u8, bool, bool);
134 
135 cb_variant!(VolumeControlCb,
136             vc_group_volume_state_callback -> VolumeControlCallbacks::GroupVolumeState,
137             i32, u8, bool, bool);
138 
139 cb_variant!(VolumeControlCb,
140             vc_device_available_callback -> VolumeControlCallbacks::DeviceAvailable,
141             RawAddress, u8);
142 
143 cb_variant!(VolumeControlCb,
144             vc_ext_audio_out_volume_offset_callback -> VolumeControlCallbacks::ExtAudioOutVolume,
145             RawAddress, u8, i16);
146 
147 cb_variant!(VolumeControlCb,
148             vc_ext_audio_out_location_callback -> VolumeControlCallbacks::ExtAudioOutLocation,
149             RawAddress, u8, u32);
150 
151 cb_variant!(VolumeControlCb,
152             vc_ext_audio_out_description_callback -> VolumeControlCallbacks::ExtAudioOutDescription,
153             RawAddress, u8, String);
154 
155 pub struct VolumeControl {
156     internal: cxx::UniquePtr<ffi::VolumeControlIntf>,
157     is_init: bool,
158     is_enabled: bool,
159 }
160 
161 // For *const u8 opaque btif
162 // SAFETY: `VolumeControlIntf` is thread-safe to make calls from.
163 unsafe impl Send for VolumeControl {}
164 
165 impl ToggleableProfile for VolumeControl {
is_enabled(&self) -> bool166     fn is_enabled(&self) -> bool {
167         self.is_enabled
168     }
169 
enable(&mut self) -> bool170     fn enable(&mut self) -> bool {
171         if self.is_enabled {
172             warn!("VolumeControl is already enabled.");
173             return false;
174         }
175 
176         self.internal.pin_mut().init();
177         self.is_enabled = true;
178         true
179     }
180 
181     #[profile_enabled_or(false)]
disable(&mut self) -> bool182     fn disable(&mut self) -> bool {
183         if !self.is_enabled {
184             warn!("VolumeControl is already disabled.");
185             return false;
186         }
187 
188         self.internal.pin_mut().cleanup();
189         self.is_enabled = false;
190         true
191     }
192 }
193 
194 impl VolumeControl {
new(intf: &BluetoothInterface) -> VolumeControl195     pub fn new(intf: &BluetoothInterface) -> VolumeControl {
196         // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface`
197         let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf> =
198             unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) };
199 
200         VolumeControl { internal: vc_if, is_init: false, is_enabled: false }
201     }
202 
is_initialized(&self) -> bool203     pub fn is_initialized(&self) -> bool {
204         self.is_init
205     }
206 
207     // `internal.init` is invoked during `ToggleableProfile::enable`
initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool208     pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool {
209         if self.is_init {
210             warn!("VolumeControl has already been initialized");
211             return false;
212         }
213 
214         if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks)))
215         {
216             panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists");
217         }
218 
219         self.is_init = true;
220 
221         true
222     }
223 
224     #[profile_enabled_or]
cleanup(&mut self)225     pub fn cleanup(&mut self) {
226         self.internal.pin_mut().cleanup();
227     }
228 
229     #[profile_enabled_or]
connect(&mut self, addr: RawAddress)230     pub fn connect(&mut self, addr: RawAddress) {
231         self.internal.pin_mut().connect(addr);
232     }
233 
234     #[profile_enabled_or]
disconnect(&mut self, addr: RawAddress)235     pub fn disconnect(&mut self, addr: RawAddress) {
236         self.internal.pin_mut().disconnect(addr);
237     }
238 
239     #[profile_enabled_or]
remove_device(&mut self, addr: RawAddress)240     pub fn remove_device(&mut self, addr: RawAddress) {
241         self.internal.pin_mut().remove_device(addr);
242     }
243 
244     #[profile_enabled_or]
set_volume(&mut self, group_id: i32, volume: u8)245     pub fn set_volume(&mut self, group_id: i32, volume: u8) {
246         self.internal.pin_mut().set_volume(group_id, volume);
247     }
248 
249     #[profile_enabled_or]
mute(&mut self, addr: RawAddress)250     pub fn mute(&mut self, addr: RawAddress) {
251         self.internal.pin_mut().mute(addr);
252     }
253 
254     #[profile_enabled_or]
unmute(&mut self, addr: RawAddress)255     pub fn unmute(&mut self, addr: RawAddress) {
256         self.internal.pin_mut().unmute(addr);
257     }
258 
259     #[profile_enabled_or]
get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8)260     pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) {
261         self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id);
262     }
263 
264     #[profile_enabled_or]
set_ext_audio_out_volume_offset( &mut self, addr: RawAddress, ext_output_id: u8, offset_val: i16, )265     pub fn set_ext_audio_out_volume_offset(
266         &mut self,
267         addr: RawAddress,
268         ext_output_id: u8,
269         offset_val: i16,
270     ) {
271         self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val);
272     }
273 
274     #[profile_enabled_or]
get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8)275     pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) {
276         self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id);
277     }
278 
279     #[profile_enabled_or]
set_ext_audio_out_location( &mut self, addr: RawAddress, ext_output_id: u8, location: u32, )280     pub fn set_ext_audio_out_location(
281         &mut self,
282         addr: RawAddress,
283         ext_output_id: u8,
284         location: u32,
285     ) {
286         self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location);
287     }
288 
289     #[profile_enabled_or]
get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8)290     pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) {
291         self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id);
292     }
293 
294     #[profile_enabled_or]
set_ext_audio_out_description( &mut self, addr: RawAddress, ext_output_id: u8, descr: String, )295     pub fn set_ext_audio_out_description(
296         &mut self,
297         addr: RawAddress,
298         ext_output_id: u8,
299         descr: String,
300     ) {
301         let c_descr = std::ffi::CString::new(descr).unwrap();
302         unsafe {
303             // SAFETY: calling an FFI where the pointer is const, no modification.
304             self.internal.pin_mut().set_ext_audio_out_description(
305                 addr,
306                 ext_output_id,
307                 c_descr.as_ptr(),
308             );
309         }
310     }
311 }
312