1 use crate::btif::{BluetoothInterface, RawAddress, ToggleableProfile, Uuid};
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         include!("types/bluetooth/uuid.h");
14         #[namespace = ""]
15         type RawAddress = crate::btif::RawAddress;
16         #[namespace = "bluetooth"]
17         type Uuid = crate::btif::Uuid;
18     }
19 
20     #[derive(Debug, Copy, Clone)]
21     pub enum BtCsisConnectionState {
22         Disconnected = 0,
23         Connecting,
24         Connected,
25         Disconnecting,
26     }
27 
28     #[derive(Debug, Copy, Clone)]
29     pub enum BtCsisGroupLockStatus {
30         Success = 0,
31         FailedInvalidGroup,
32         FailedGroupEmpty,
33         FailedGroupNotConnected,
34         FailedLockedByOther,
35         FailedOtherReason,
36         LockedGroupMemberLost,
37     }
38 
39     unsafe extern "C++" {
40         include!("csis/csis_shim.h");
41 
42         type CsisClientIntf;
43 
GetCsisClientProfile(btif: *const u8) -> UniquePtr<CsisClientIntf>44         unsafe fn GetCsisClientProfile(btif: *const u8) -> UniquePtr<CsisClientIntf>;
45 
init(self: Pin<&mut CsisClientIntf>)46         fn init(self: Pin<&mut CsisClientIntf>);
connect(self: Pin<&mut CsisClientIntf>, addr: RawAddress)47         fn connect(self: Pin<&mut CsisClientIntf>, addr: RawAddress);
disconnect(self: Pin<&mut CsisClientIntf>, addr: RawAddress)48         fn disconnect(self: Pin<&mut CsisClientIntf>, addr: RawAddress);
lock_group(self: Pin<&mut CsisClientIntf>, group_id: i32, lock: bool)49         fn lock_group(self: Pin<&mut CsisClientIntf>, group_id: i32, lock: bool);
remove_device(self: Pin<&mut CsisClientIntf>, addr: RawAddress)50         fn remove_device(self: Pin<&mut CsisClientIntf>, addr: RawAddress);
cleanup(self: Pin<&mut CsisClientIntf>)51         fn cleanup(self: Pin<&mut CsisClientIntf>);
52     }
53 
54     extern "Rust" {
csis_connection_state_callback(addr: RawAddress, state: BtCsisConnectionState)55         fn csis_connection_state_callback(addr: RawAddress, state: BtCsisConnectionState);
csis_device_available_callback( addr: RawAddress, group_id: i32, group_size: i32, rank: i32, uuid: Uuid, )56         fn csis_device_available_callback(
57             addr: RawAddress,
58             group_id: i32,
59             group_size: i32,
60             rank: i32,
61             uuid: Uuid,
62         );
csis_set_member_available_callback(addr: RawAddress, group_id: i32)63         fn csis_set_member_available_callback(addr: RawAddress, group_id: i32);
csis_group_lock_changed_callback( group_id: i32, locked: bool, status: BtCsisGroupLockStatus, )64         fn csis_group_lock_changed_callback(
65             group_id: i32,
66             locked: bool,
67             status: BtCsisGroupLockStatus,
68         );
69     }
70 }
71 
72 pub type BtCsisConnectionState = ffi::BtCsisConnectionState;
73 pub type BtCsisGroupLockStatus = ffi::BtCsisGroupLockStatus;
74 
75 #[derive(Debug)]
76 pub enum CsisClientCallbacks {
77     ConnectionState(RawAddress, BtCsisConnectionState),
78     DeviceAvailable(RawAddress, i32, i32, i32, Uuid),
79     SetMemberAvailable(RawAddress, i32),
80     GroupLockChanged(i32, bool, BtCsisGroupLockStatus),
81 }
82 
83 pub struct CsisClientCallbacksDispatcher {
84     pub dispatch: Box<dyn Fn(CsisClientCallbacks) + Send>,
85 }
86 
87 type CsisClientCb = Arc<Mutex<CsisClientCallbacksDispatcher>>;
88 
89 cb_variant!(CsisClientCb,
90             csis_connection_state_callback -> CsisClientCallbacks::ConnectionState,
91             RawAddress, BtCsisConnectionState);
92 
93 cb_variant!(CsisClientCb,
94             csis_device_available_callback -> CsisClientCallbacks::DeviceAvailable,
95             RawAddress, i32, i32, i32, Uuid);
96 
97 cb_variant!(CsisClientCb,
98             csis_set_member_available_callback -> CsisClientCallbacks::SetMemberAvailable,
99             RawAddress, i32);
100 
101 cb_variant!(CsisClientCb,
102             csis_group_lock_changed_callback -> CsisClientCallbacks::GroupLockChanged,
103             i32, bool, BtCsisGroupLockStatus);
104 
105 pub struct CsisClient {
106     internal: cxx::UniquePtr<ffi::CsisClientIntf>,
107     is_init: bool,
108     is_enabled: bool,
109 }
110 
111 // For *const u8 opaque btif
112 // SAFETY: `CsisClientIntf` is thread-safe to make calls from.
113 unsafe impl Send for CsisClient {}
114 
115 impl ToggleableProfile for CsisClient {
is_enabled(&self) -> bool116     fn is_enabled(&self) -> bool {
117         self.is_enabled
118     }
119 
enable(&mut self) -> bool120     fn enable(&mut self) -> bool {
121         if self.is_enabled {
122             warn!("CsisClient is already enabled.");
123             return false;
124         }
125 
126         self.internal.pin_mut().init();
127         self.is_enabled = true;
128         true
129     }
130 
131     #[profile_enabled_or(false)]
disable(&mut self) -> bool132     fn disable(&mut self) -> bool {
133         if !self.is_enabled {
134             warn!("CsisClient is already disabled.");
135             return false;
136         }
137 
138         self.internal.pin_mut().cleanup();
139         self.is_enabled = false;
140         true
141     }
142 }
143 
144 impl CsisClient {
new(intf: &BluetoothInterface) -> CsisClient145     pub fn new(intf: &BluetoothInterface) -> CsisClient {
146         let csis_if: cxx::UniquePtr<ffi::CsisClientIntf>;
147 
148         // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface`
149         csis_if = unsafe { ffi::GetCsisClientProfile(intf.as_raw_ptr()) };
150 
151         CsisClient { internal: csis_if, is_init: false, is_enabled: false }
152     }
153 
is_initialized(&self) -> bool154     pub fn is_initialized(&self) -> bool {
155         self.is_init
156     }
157 
158     // `internal.init` is invoked during `ToggleableProfile::enable`
initialize(&mut self, callbacks: CsisClientCallbacksDispatcher) -> bool159     pub fn initialize(&mut self, callbacks: CsisClientCallbacksDispatcher) -> bool {
160         if self.is_init {
161             warn!("CsisClient has already been initialized");
162             return false;
163         }
164 
165         if get_dispatchers().lock().unwrap().set::<CsisClientCb>(Arc::new(Mutex::new(callbacks))) {
166             panic!("Tried to set dispatcher for CsisClient callbacks while it already exists");
167         }
168 
169         self.is_init = true;
170 
171         true
172     }
173 
174     #[profile_enabled_or]
cleanup(&mut self)175     pub fn cleanup(&mut self) {
176         self.internal.pin_mut().cleanup();
177     }
178 
179     #[profile_enabled_or]
connect(&mut self, addr: RawAddress)180     pub fn connect(&mut self, addr: RawAddress) {
181         self.internal.pin_mut().connect(addr);
182     }
183 
184     #[profile_enabled_or]
disconnect(&mut self, addr: RawAddress)185     pub fn disconnect(&mut self, addr: RawAddress) {
186         self.internal.pin_mut().disconnect(addr);
187     }
188 
189     #[profile_enabled_or]
lock_group(&mut self, group_id: i32, lock: bool)190     pub fn lock_group(&mut self, group_id: i32, lock: bool) {
191         self.internal.pin_mut().lock_group(group_id, lock);
192     }
193 
194     #[profile_enabled_or]
remove_device(&mut self, addr: RawAddress)195     pub fn remove_device(&mut self, addr: RawAddress) {
196         self.internal.pin_mut().remove_device(addr);
197     }
198 }
199