1
+ use std:: collections:: HashMap ;
2
+ use std:: pin:: Pin ;
3
+ use std:: sync:: atomic:: { AtomicI32 , Ordering } ;
4
+ use std:: sync:: { Arc , Mutex } ;
5
+ use std:: task:: { Context , Poll } ;
6
+
7
+ use futures_channel:: mpsc:: { Receiver , Sender } ;
1
8
use futures_core:: Stream ;
2
9
use futures_lite:: { stream, StreamExt } ;
10
+ use jni_glue:: { Argument , ByteArray , Env , Global , Local , PrimitiveArray , VM } ;
11
+ use tracing:: { info, warn} ;
3
12
use uuid:: Uuid ;
4
13
5
- use crate :: { AdapterEvent , AdvertisingDevice , ConnectionEvent , Device , DeviceId , Result } ;
14
+ use super :: bindings:: android:: bluetooth:: le:: { BluetoothLeScanner , ScanResult } ;
15
+ use super :: bindings:: android:: bluetooth:: { BluetoothAdapter , BluetoothManager } ;
16
+ use super :: bindings:: android:: os:: ParcelUuid ;
17
+ use super :: bindings:: com:: github:: alexmoon:: bluest:: android:: BluestScanCallback ;
18
+ use super :: device:: DeviceImpl ;
19
+ use super :: JavaIterator ;
20
+ use crate :: android:: bindings:: java:: util:: Map_Entry ;
21
+ use crate :: util:: defer;
22
+ use crate :: {
23
+ AdapterEvent , AdvertisementData , AdvertisingDevice , ConnectionEvent , Device , DeviceId , ManufacturerData , Result ,
24
+ } ;
25
+
26
+ struct AdapterInner {
27
+ manager : Global < BluetoothManager > ,
28
+ _adapter : Global < BluetoothAdapter > ,
29
+ le_scanner : Global < BluetoothLeScanner > ,
30
+ }
6
31
7
32
#[ derive( Clone ) ]
8
- pub struct AdapterImpl { }
33
+ pub struct AdapterImpl {
34
+ inner : Arc < AdapterInner > ,
35
+ }
36
+
9
37
impl AdapterImpl {
10
- pub async fn default ( ) -> Option < Self > {
11
- Some ( Self { } )
38
+ pub unsafe fn new ( vm : * mut jni_glue:: sys:: JavaVM , manager : jni_glue:: sys:: jobject ) -> Result < Self > {
39
+ let vm = VM :: from_raw ( vm) ;
40
+ let manager: Global < BluetoothManager > = Global :: from_raw ( vm, manager) ;
41
+
42
+ vm. with_env ( |env| {
43
+ let local_manager = manager. with ( env) ;
44
+ let adapter = local_manager. getAdapter ( ) ?. unwrap ( ) ;
45
+ let le_scanner = adapter. getBluetoothLeScanner ( ) ?. unwrap ( ) ;
46
+
47
+ Ok ( Self {
48
+ inner : Arc :: new ( AdapterInner {
49
+ _adapter : adapter. as_global ( ) ,
50
+ le_scanner : le_scanner. as_global ( ) ,
51
+ manager : manager. clone ( ) ,
52
+ } ) ,
53
+ } )
54
+ } )
12
55
}
13
56
14
57
pub ( crate ) async fn events ( & self ) -> Result < impl Stream < Item = Result < AdapterEvent > > + Send + Unpin + ' _ > {
@@ -35,7 +78,27 @@ impl AdapterImpl {
35
78
& ' a self ,
36
79
_services : & ' a [ Uuid ] ,
37
80
) -> Result < impl Stream < Item = AdvertisingDevice > + Send + Unpin + ' a > {
38
- Ok ( stream:: empty ( ) ) // TODO
81
+ self . inner . manager . vm ( ) . with_env ( |env| {
82
+ let receiver = SCAN_CALLBACKS . allocate ( ) ;
83
+ let callback = BluestScanCallback :: new ( env, receiver. id ) ?;
84
+ let callback_global = callback. as_global ( ) ;
85
+ let scanner = self . inner . le_scanner . with ( env) ;
86
+ scanner. startScan_ScanCallback ( & * * callback) ?;
87
+
88
+ let guard = defer ( move || {
89
+ self . inner . manager . vm ( ) . with_env ( |env| {
90
+ let callback = callback_global. with ( env) ;
91
+ let scanner = self . inner . le_scanner . with ( env) ;
92
+ scanner. stopScan_ScanCallback ( & * * callback) . unwrap ( ) ;
93
+ info ! ( "stopped scan" ) ;
94
+ } ) ;
95
+ } ) ;
96
+
97
+ Ok ( receiver. map ( move |x| {
98
+ let _guard = & guard;
99
+ x
100
+ } ) )
101
+ } )
39
102
}
40
103
41
104
pub async fn discover_devices < ' a > (
@@ -91,3 +154,152 @@ impl std::fmt::Debug for AdapterImpl {
91
154
f. debug_tuple ( "Adapter" ) . finish ( )
92
155
}
93
156
}
157
+
158
+ static SCAN_CALLBACKS : CallbackRouter < AdvertisingDevice > = CallbackRouter :: new ( ) ;
159
+
160
+ struct CallbackRouter < T : Send + ' static > {
161
+ map : Mutex < Option < HashMap < i32 , Sender < T > > > > ,
162
+ next_id : AtomicI32 ,
163
+ }
164
+
165
+ impl < T : Send + ' static > CallbackRouter < T > {
166
+ const fn new ( ) -> Self {
167
+ Self {
168
+ map : Mutex :: new ( None ) ,
169
+ next_id : AtomicI32 :: new ( 0 ) ,
170
+ }
171
+ }
172
+
173
+ fn allocate ( & ' static self ) -> CallbackReceiver < T > {
174
+ let id = self . next_id . fetch_add ( 1 , Ordering :: Relaxed ) ;
175
+ let ( sender, receiver) = futures_channel:: mpsc:: channel ( 16 ) ;
176
+ self . map
177
+ . lock ( )
178
+ . unwrap ( )
179
+ . get_or_insert_with ( Default :: default)
180
+ . insert ( id, sender) ;
181
+
182
+ CallbackReceiver {
183
+ router : self ,
184
+ id,
185
+ receiver,
186
+ }
187
+ }
188
+
189
+ fn callback ( & ' static self , id : i32 , val : T ) {
190
+ if let Some ( sender) = self . map . lock ( ) . unwrap ( ) . as_mut ( ) . unwrap ( ) . get_mut ( & id) {
191
+ if let Err ( e) = sender. try_send ( val) {
192
+ warn ! ( "failed to send scan callback: {:?}" , e)
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ struct CallbackReceiver < T : Send + ' static > {
199
+ router : & ' static CallbackRouter < T > ,
200
+ id : i32 ,
201
+ receiver : Receiver < T > ,
202
+ }
203
+
204
+ impl < T : Send + ' static > Stream for CallbackReceiver < T > {
205
+ type Item = T ;
206
+
207
+ fn poll_next ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
208
+ // safety: this is just a manually-written pin projection.
209
+ let receiver = unsafe { Pin :: new_unchecked ( & mut self . get_unchecked_mut ( ) . receiver ) } ;
210
+ receiver. poll_next ( cx)
211
+ }
212
+ }
213
+
214
+ impl < T : Send > Drop for CallbackReceiver < T > {
215
+ fn drop ( & mut self ) {
216
+ self . router . map . lock ( ) . unwrap ( ) . as_mut ( ) . unwrap ( ) . remove ( & self . id ) ;
217
+ }
218
+ }
219
+
220
+ #[ no_mangle]
221
+ pub extern "system" fn Java_com_github_alexmoon_bluest_android_BluestScanCallback_nativeOnScanResult (
222
+ env : Env < ' _ > ,
223
+ _class : * mut ( ) , // self class, ignore
224
+ id : i32 ,
225
+ callback_type : i32 ,
226
+ scan_result : Argument < ScanResult > ,
227
+ ) {
228
+ let scan_result = unsafe { scan_result. with_unchecked ( env) } . unwrap ( ) ;
229
+
230
+ tracing:: info!( "got callback! {} {}" , id, callback_type) ;
231
+
232
+ let scan_record = scan_result. getScanRecord ( ) . unwrap ( ) . unwrap ( ) ;
233
+ let device = scan_result. getDevice ( ) . unwrap ( ) . unwrap ( ) ;
234
+
235
+ fn convert_uuid ( uuid : Local < ' _ , ParcelUuid > ) -> Uuid {
236
+ let uuid = uuid. getUuid ( ) . unwrap ( ) . unwrap ( ) ;
237
+ let lsb = uuid. getLeastSignificantBits ( ) . unwrap ( ) as u64 ;
238
+ let msb = uuid. getMostSignificantBits ( ) . unwrap ( ) as u64 ;
239
+ Uuid :: from_u64_pair ( msb, lsb)
240
+ }
241
+
242
+ let address = device. getAddress ( ) . unwrap ( ) . unwrap ( ) . to_string_lossy ( ) ;
243
+ let rssi = scan_result. getRssi ( ) . unwrap ( ) ;
244
+ let is_connectable = scan_result. isConnectable ( ) . unwrap ( ) ;
245
+ let local_name = scan_record. getDeviceName ( ) . unwrap ( ) . map ( |s| s. to_string_lossy ( ) ) ;
246
+ let tx_power_level = scan_record. getTxPowerLevel ( ) . unwrap ( ) ;
247
+
248
+ // Services
249
+ let mut services = Vec :: new ( ) ;
250
+ if let Some ( uuids) = scan_record. getServiceUuids ( ) . unwrap ( ) {
251
+ for uuid in JavaIterator ( uuids. iterator ( ) . unwrap ( ) . unwrap ( ) ) {
252
+ services. push ( convert_uuid ( uuid. cast ( ) . unwrap ( ) ) )
253
+ }
254
+ }
255
+
256
+ // Service data
257
+ let mut service_data = HashMap :: new ( ) ;
258
+ let sd = scan_record. getServiceData ( ) . unwrap ( ) . unwrap ( ) ;
259
+ let sd = sd. entrySet ( ) . unwrap ( ) . unwrap ( ) ;
260
+ for entry in JavaIterator ( sd. iterator ( ) . unwrap ( ) . unwrap ( ) ) {
261
+ let entry: Local < Map_Entry > = entry. cast ( ) . unwrap ( ) ;
262
+ let key: Local < ParcelUuid > = entry. getKey ( ) . unwrap ( ) . unwrap ( ) . cast ( ) . unwrap ( ) ;
263
+ let val: Local < ByteArray > = entry. getValue ( ) . unwrap ( ) . unwrap ( ) . cast ( ) . unwrap ( ) ;
264
+ service_data. insert ( convert_uuid ( key) , val. as_vec ( ) . into_iter ( ) . map ( |i| i as u8 ) . collect ( ) ) ;
265
+ }
266
+
267
+ // Manufacturer data
268
+ let mut manufacturer_data = None ;
269
+ let msd = scan_record. getManufacturerSpecificData ( ) . unwrap ( ) . unwrap ( ) ;
270
+ // TODO there can be multiple manufacturer data entries, but the bluest API only supports one. So grab just the first.
271
+ if msd. size ( ) . unwrap ( ) != 0 {
272
+ let val: Local < ' _ , ByteArray > = msd. valueAt ( 0 ) . unwrap ( ) . unwrap ( ) . cast ( ) . unwrap ( ) ;
273
+ manufacturer_data = Some ( ManufacturerData {
274
+ company_id : msd. keyAt ( 0 ) . unwrap ( ) as _ ,
275
+ data : val. as_vec ( ) . into_iter ( ) . map ( |i| i as u8 ) . collect ( ) ,
276
+ } ) ;
277
+ }
278
+
279
+ let device_id = DeviceId ( address) ;
280
+
281
+ let d = AdvertisingDevice {
282
+ device : Device ( DeviceImpl { id : device_id } ) ,
283
+ adv_data : AdvertisementData {
284
+ is_connectable,
285
+ local_name,
286
+ manufacturer_data, // TODO, SparseArray is cursed.
287
+ service_data,
288
+ services,
289
+ tx_power_level : Some ( tx_power_level as _ ) ,
290
+ } ,
291
+ rssi : Some ( rssi as _ ) ,
292
+ } ;
293
+ SCAN_CALLBACKS . callback ( id, d) ;
294
+ }
295
+
296
+ #[ no_mangle]
297
+ pub extern "system" fn Java_com_github_alexmoon_bluest_android_BluestScanCallback_nativeOnScanFailed (
298
+ _env : Env < ' _ > ,
299
+ _class : * mut ( ) , // self class, ignore
300
+ id : i32 ,
301
+ error_code : i32 ,
302
+ ) {
303
+ tracing:: error!( "got scan fail! {} {}" , id, error_code) ;
304
+ todo ! ( )
305
+ }
0 commit comments