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         let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf>;
197 
198         // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface`
199         vc_if = unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) };
200 
201         VolumeControl { internal: vc_if, is_init: false, is_enabled: false }
202     }
203 
is_initialized(&self) -> bool204     pub fn is_initialized(&self) -> bool {
205         self.is_init
206     }
207 
208     // `internal.init` is invoked during `ToggleableProfile::enable`
initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool209     pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool {
210         if self.is_init {
211             warn!("VolumeControl has already been initialized");
212             return false;
213         }
214 
215         if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks)))
216         {
217             panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists");
218         }
219 
220         self.is_init = true;
221 
222         true
223     }
224 
225     #[profile_enabled_or]
cleanup(&mut self)226     pub fn cleanup(&mut self) {
227         self.internal.pin_mut().cleanup();
228     }
229 
230     #[profile_enabled_or]
connect(&mut self, addr: RawAddress)231     pub fn connect(&mut self, addr: RawAddress) {
232         self.internal.pin_mut().connect(addr);
233     }
234 
235     #[profile_enabled_or]
disconnect(&mut self, addr: RawAddress)236     pub fn disconnect(&mut self, addr: RawAddress) {
237         self.internal.pin_mut().disconnect(addr);
238     }
239 
240     #[profile_enabled_or]
remove_device(&mut self, addr: RawAddress)241     pub fn remove_device(&mut self, addr: RawAddress) {
242         self.internal.pin_mut().remove_device(addr);
243     }
244 
245     #[profile_enabled_or]
set_volume(&mut self, group_id: i32, volume: u8)246     pub fn set_volume(&mut self, group_id: i32, volume: u8) {
247         self.internal.pin_mut().set_volume(group_id, volume);
248     }
249 
250     #[profile_enabled_or]
mute(&mut self, addr: RawAddress)251     pub fn mute(&mut self, addr: RawAddress) {
252         self.internal.pin_mut().mute(addr);
253     }
254 
255     #[profile_enabled_or]
unmute(&mut self, addr: RawAddress)256     pub fn unmute(&mut self, addr: RawAddress) {
257         self.internal.pin_mut().unmute(addr);
258     }
259 
260     #[profile_enabled_or]
get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8)261     pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) {
262         self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id);
263     }
264 
265     #[profile_enabled_or]
set_ext_audio_out_volume_offset( &mut self, addr: RawAddress, ext_output_id: u8, offset_val: i16, )266     pub fn set_ext_audio_out_volume_offset(
267         &mut self,
268         addr: RawAddress,
269         ext_output_id: u8,
270         offset_val: i16,
271     ) {
272         self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val);
273     }
274 
275     #[profile_enabled_or]
get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8)276     pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) {
277         self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id);
278     }
279 
280     #[profile_enabled_or]
set_ext_audio_out_location( &mut self, addr: RawAddress, ext_output_id: u8, location: u32, )281     pub fn set_ext_audio_out_location(
282         &mut self,
283         addr: RawAddress,
284         ext_output_id: u8,
285         location: u32,
286     ) {
287         self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location);
288     }
289 
290     #[profile_enabled_or]
get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8)291     pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) {
292         self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id);
293     }
294 
295     #[profile_enabled_or]
set_ext_audio_out_description( &mut self, addr: RawAddress, ext_output_id: u8, descr: String, )296     pub fn set_ext_audio_out_description(
297         &mut self,
298         addr: RawAddress,
299         ext_output_id: u8,
300         descr: String,
301     ) {
302         let c_descr = std::ffi::CString::new(descr).unwrap();
303         unsafe {
304             // SAFETY: calling an FFI where the pointer is const, no modification.
305             self.internal.pin_mut().set_ext_audio_out_description(
306                 addr,
307                 ext_output_id,
308                 c_descr.as_ptr(),
309             );
310         }
311     }
312 }
313