7
7
import android .bluetooth .BluetoothGattCallback ;
8
8
import android .bluetooth .BluetoothGattCharacteristic ;
9
9
import android .bluetooth .BluetoothGattDescriptor ;
10
+ import android .bluetooth .BluetoothGattService ;
10
11
import android .bluetooth .BluetoothManager ;
11
12
import android .bluetooth .BluetoothProfile ;
12
13
import android .bluetooth .le .ScanCallback ;
18
19
import android .os .Build ;
19
20
import android .util .Log ;
20
21
22
+ import com .google .protobuf .ByteString ;
21
23
import com .google .protobuf .InvalidProtocolBufferException ;
22
24
23
25
import java .util .HashMap ;
24
- import java .util .Iterator ;
25
26
import java .util .List ;
26
27
import java .util .Map ;
28
+ import java .util .UUID ;
27
29
28
30
import io .flutter .plugin .common .EventChannel ;
29
31
import io .flutter .plugin .common .EventChannel .EventSink ;
@@ -45,6 +47,8 @@ public class FlutterBluePlugin implements MethodCallHandler {
45
47
private final MethodChannel channel ;
46
48
private final EventChannel stateChannel ;
47
49
private final EventChannel scanResultChannel ;
50
+ private final EventChannel servicesDiscoveredChannel ;
51
+ private final EventChannel characteristicReadChannel ;
48
52
private final BluetoothManager mBluetoothManager ;
49
53
private BluetoothAdapter mBluetoothAdapter ;
50
54
private final Map <String , Result > mConnectionRequests = new HashMap <>();
@@ -62,11 +66,15 @@ public static void registerWith(Registrar registrar) {
62
66
this .channel = new MethodChannel (registrar .messenger (), NAMESPACE +"/methods" );
63
67
this .stateChannel = new EventChannel (registrar .messenger (), NAMESPACE +"/state" );
64
68
this .scanResultChannel = new EventChannel (registrar .messenger (), NAMESPACE +"/scanResult" );
69
+ this .servicesDiscoveredChannel = new EventChannel (registrar .messenger (), NAMESPACE +"/servicesDiscovered" );
70
+ this .characteristicReadChannel = new EventChannel (registrar .messenger (), NAMESPACE +"/characteristicRead" );
65
71
this .mBluetoothManager = (BluetoothManager ) r .activity ().getSystemService (Context .BLUETOOTH_SERVICE );
66
72
this .mBluetoothAdapter = mBluetoothManager .getAdapter ();
67
73
channel .setMethodCallHandler (this );
68
74
stateChannel .setStreamHandler (stateHandler );
69
75
scanResultChannel .setStreamHandler (scanResultsHandler );
76
+ servicesDiscoveredChannel .setStreamHandler (servicesDiscoveredHandler );
77
+ characteristicReadChannel .setStreamHandler (characteristicReadHandler );
70
78
}
71
79
72
80
@ Override
@@ -142,7 +150,6 @@ public void onMethodCall(MethodCall call, Result result) {
142
150
143
151
case "connect" :
144
152
{
145
- printConnectionRequests ();
146
153
byte [] data = call .arguments ();
147
154
Protos .ConnectOptions options ;
148
155
try {
@@ -188,7 +195,6 @@ public void onMethodCall(MethodCall call, Result result) {
188
195
189
196
case "disconnect" :
190
197
{
191
- printConnectionRequests ();
192
198
String deviceId = (String )call .arguments ;
193
199
BluetoothGatt gattServer = mGattServers .remove (deviceId );
194
200
if (gattServer != null ) {
@@ -204,20 +210,94 @@ public void onMethodCall(MethodCall call, Result result) {
204
210
break ;
205
211
}
206
212
207
- default :
213
+ case "discoverServices" :
208
214
{
209
- result .notImplemented ();
215
+ String deviceId = (String )call .arguments ;
216
+ BluetoothGatt gattServer = mGattServers .get (deviceId );
217
+ if (gattServer == null ) {
218
+ result .error ("discover_services_error" , "no instance of BluetoothGatt, have you connected first?" , null );
219
+ return ;
220
+ }
221
+ if (gattServer .discoverServices ()) {
222
+ result .success (null );
223
+ } else {
224
+ result .error ("discover_services_error" , "unknown reason" , null );
225
+ }
210
226
break ;
211
227
}
212
- }
213
- }
214
228
229
+ case "services" :
230
+ {
231
+ String deviceId = (String )call .arguments ;
232
+ BluetoothGatt gattServer = mGattServers .get (deviceId );
233
+ if (gattServer == null ) {
234
+ result .error ("get_services_error" , "no instance of BluetoothGatt, have you connected first?" , null );
235
+ return ;
236
+ }
237
+ if (gattServer .getServices ().isEmpty ()) {
238
+ result .error ("get_services_error" , "services are empty, have you called discoverServices() yet?" , null );
239
+ return ;
240
+ }
241
+ Protos .DiscoverServicesResult .Builder p = Protos .DiscoverServicesResult .newBuilder ();
242
+ p .setRemoteId (deviceId );
243
+ for (BluetoothGattService s : gattServer .getServices ()){
244
+ p .addServices (ProtoMaker .from (gattServer .getDevice (), s ));
245
+ }
246
+ result .success (p .build ().toByteArray ());
247
+ break ;
248
+ }
215
249
216
- private void printConnectionRequests () {
217
- synchronized (mConnectionRequests ) {
218
- for (Iterator <Map .Entry <String , Result >> it = mConnectionRequests .entrySet ().iterator (); it .hasNext (); ) {
219
- Map .Entry <String , Result > entry = it .next ();
220
- Log .d (TAG , "printConnectionRequests: deviceId:" + entry .getKey ());
250
+ case "readCharacteristic" :
251
+ {
252
+ byte [] data = call .arguments ();
253
+ Protos .ReadAttributeRequest request ;
254
+ try {
255
+ request = Protos .ReadAttributeRequest .newBuilder ().mergeFrom (data ).build ();
256
+ } catch (InvalidProtocolBufferException e ) {
257
+ result .error ("RuntimeException" , e .getMessage (), e );
258
+ break ;
259
+ }
260
+ BluetoothGatt gattServer = mGattServers .get (request .getRemoteId ());
261
+ if (gattServer == null ) {
262
+ result .error ("read_characteristic_error" , "no instance of BluetoothGatt, have you connected first?" , null );
263
+ return ;
264
+ }
265
+ BluetoothGattService primaryService = gattServer .getService (UUID .fromString (request .getServiceUuid ()));
266
+ if (primaryService == null ) {
267
+ result .error ("read_characteristic_error" , "service (" + request .getServiceUuid () + ") could not be located on the device" , null );
268
+ return ;
269
+ }
270
+ BluetoothGattService secondaryService = null ;
271
+ if (request .getSecondaryServiceUuid ().length () > 0 ) {
272
+ for (BluetoothGattService s : primaryService .getIncludedServices ()){
273
+ if (s .getUuid ().equals (UUID .fromString (request .getSecondaryServiceUuid ()))){
274
+ secondaryService = s ;
275
+ }
276
+ }
277
+ if (secondaryService == null ) {
278
+ result .error ("read_characteristic_error" , "secondary service (" + request .getSecondaryServiceUuid () + ") could not be located on the device" , null );
279
+ return ;
280
+ }
281
+ }
282
+ BluetoothGattService service = (secondaryService != null ) ? secondaryService : primaryService ;
283
+ BluetoothGattCharacteristic characteristic = service .getCharacteristic (UUID .fromString (request .getUuid ()));
284
+ if (characteristic == null ) {
285
+ result .error ("read_characteristic_error" , "characteristic (" + request .getUuid () + ") could not be located in the service (" +service .getUuid ().toString ()+")" , null );
286
+ return ;
287
+ }
288
+
289
+ if (gattServer .readCharacteristic (characteristic )) {
290
+ result .success (null );
291
+ } else {
292
+ result .error ("read_characteristic_error" , "unknown reason. occurs if readCharacteristic was called before last read finished." , null );
293
+ }
294
+ break ;
295
+ }
296
+
297
+ default :
298
+ {
299
+ result .notImplemented ();
300
+ break ;
221
301
}
222
302
}
223
303
}
@@ -373,6 +453,32 @@ public void onCancel(Object o) {
373
453
}
374
454
};
375
455
456
+ private EventSink servicesDiscoveredSink ;
457
+ private final StreamHandler servicesDiscoveredHandler = new StreamHandler () {
458
+ @ Override
459
+ public void onListen (Object o , EventChannel .EventSink eventSink ) {
460
+ servicesDiscoveredSink = eventSink ;
461
+ }
462
+
463
+ @ Override
464
+ public void onCancel (Object o ) {
465
+ servicesDiscoveredSink = null ;
466
+ }
467
+ };
468
+
469
+ private EventSink characteristicReadSink ;
470
+ private final StreamHandler characteristicReadHandler = new StreamHandler () {
471
+ @ Override
472
+ public void onListen (Object o , EventChannel .EventSink eventSink ) {
473
+ characteristicReadSink = eventSink ;
474
+ }
475
+
476
+ @ Override
477
+ public void onCancel (Object o ) {
478
+ characteristicReadSink = null ;
479
+ }
480
+ };
481
+
376
482
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback () {
377
483
@ Override
378
484
public void onConnectionStateChange (BluetoothGatt gatt , int status , int newState ) {
@@ -398,12 +504,44 @@ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState
398
504
399
505
@ Override
400
506
public void onServicesDiscovered (BluetoothGatt gatt , int status ) {
401
- Log .d (TAG , "onServicesDiscovered: " );
507
+ Log .d (TAG , "onServicesDiscovered: " + gatt .getServices ().size () + " sink:" + servicesDiscoveredSink );
508
+ if (servicesDiscoveredSink != null ) {
509
+ Protos .DiscoverServicesResult .Builder p = Protos .DiscoverServicesResult .newBuilder ();
510
+ p .setRemoteId (gatt .getDevice ().getAddress ());
511
+ for (BluetoothGattService s : gatt .getServices ()) {
512
+ p .addServices (ProtoMaker .from (gatt .getDevice (), s ));
513
+ }
514
+ servicesDiscoveredSink .success (p .build ().toByteArray ());
515
+ }
402
516
}
403
517
404
518
@ Override
405
519
public void onCharacteristicRead (BluetoothGatt gatt , BluetoothGattCharacteristic characteristic , int status ) {
406
520
Log .d (TAG , "onCharacteristicRead: " );
521
+ if (characteristicReadSink != null ) {
522
+ // Rebuild the ReadAttributeRequest and send back along with response
523
+ Protos .ReadAttributeRequest .Builder q = Protos .ReadAttributeRequest .newBuilder ();
524
+ q .setRemoteId (gatt .getDevice ().getAddress ());
525
+ q .setUuid (characteristic .getUuid ().toString ());
526
+ if (characteristic .getService ().getType () == BluetoothGattService .SERVICE_TYPE_PRIMARY ) {
527
+ q .setServiceUuid (characteristic .getService ().getUuid ().toString ());
528
+ } else {
529
+ // Reverse search to find service
530
+ for (BluetoothGattService s : gatt .getServices ()) {
531
+ for (BluetoothGattService ss : s .getIncludedServices ()) {
532
+ if (ss .getUuid ().equals (characteristic .getService ().getUuid ())){
533
+ q .setServiceUuid (s .getUuid ().toString ());
534
+ q .setSecondaryServiceUuid (ss .getUuid ().toString ());
535
+ break ;
536
+ }
537
+ }
538
+ }
539
+ }
540
+ Protos .ReadAttributeResponse .Builder p = Protos .ReadAttributeResponse .newBuilder ();
541
+ p .setRequest (q );
542
+ p .setValue (ByteString .copyFrom (characteristic .getValue ()));
543
+ characteristicReadSink .success (p .build ().toByteArray ());
544
+ }
407
545
}
408
546
409
547
@ Override
0 commit comments