react-native-ble-manager 7.6.2 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Before open an issue**
11
+ - Check the closed issues, your question maybe is not new.
12
+ - We can't debug or reproduce hardware issue.
13
+ - If the library is not working 99% you did something wrong, in the code, installation or in the phone permissions.
14
+
15
+ **Describe the bug**
16
+ A clear and concise description of what the bug is.
17
+
18
+ **To Reproduce**
19
+ Steps to reproduce the behavior:
20
+ 1. Go to '...'
21
+ 2. Click on '....'
22
+ 3. Scroll down to '....'
23
+ 4. See error
24
+
25
+ **Expected behavior**
26
+ A clear and concise description of what you expected to happen.
27
+
28
+ **Screenshots**
29
+ If applicable, add screenshots to help explain your problem.
30
+
31
+ **Smartphone (please complete the following information):**
32
+ - Device: [e.g. iPhone6]
33
+ - OS: [e.g. iOS8.1]
34
+ - react-native-ble-manager version:
35
+ - react-native version:
36
+
37
+ **Additional context**
38
+ Add any other context about the problem here.
package/README.md CHANGED
@@ -22,23 +22,45 @@ RN 0.30-0.39 supported until 2.4.3
22
22
 
23
23
  ```shell
24
24
  npm i --save react-native-ble-manager
25
- npx react-native link
26
25
  ```
26
+ The library support the react native autolink feature.
27
27
 
28
28
 
29
29
  ##### Android - Update Manifest
30
30
 
31
31
  ```xml
32
32
  // file: android/app/src/main/AndroidManifest.xml
33
- ...
34
- <uses-permission android:name="android.permission.BLUETOOTH"/>
35
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
36
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
33
+ <!-- Add xmlns:tools -->
34
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
35
+ xmlns:tools="http://schemas.android.com/tools"
36
+ package="YOUR_PACKAGE_NAME">
37
+
38
+ <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
39
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
40
+
41
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28"/>
42
+ <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" tools:targetApi="Q"/>
43
+
44
+ <!-- Only when targeting Android 12 or higher -->
45
+ <!-- Please make sure you read the following documentation to have a
46
+ better understanging of the new permissions.
47
+ https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location
48
+ -->
49
+
50
+ <!-- If your app doesn't use Bluetooth scan results to derive physical location information,
51
+ you can strongly assert that your app
52
+ doesn't derive physical location. -->
53
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
54
+ android:usesPermissionFlags="neverForLocation"
55
+ tools:targetApi="s" />
56
+
57
+ <!-- Needed only if your app looks for Bluetooth devices. -->
58
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
59
+ <!-- Needed only if your app makes the device discoverable to Bluetooth devices. -->
60
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
37
61
  ...
38
62
  ```
39
63
 
40
- In Android API 29 >= you need to use "ACCESS_FINE_LOCATION" instead of "ACCESS_COARSE_LOCATION".
41
-
42
64
  If you need communication while the app is not in the foreground you need the "ACCESS_BACKGROUND_LOCATION" permission.
43
65
 
44
66
  ##### iOS - Update Info.plist
@@ -53,6 +75,7 @@ In iOS >= 13 you need to add the `NSBluetoothAlwaysUsageDescription` string key.
53
75
  - Android API >= 29 require the ACCESS_FINE_LOCATION permission to scan for peripherals.
54
76
  React-Native 0.63.X started targeting Android API 29.
55
77
  - Before write, read or start notification you need to call `retrieveServices` method
78
+ - Because location and bluetooth permissions are runtime permissions, you **must** request these permissions at runtime along with declaring them in your manifest.
56
79
 
57
80
  ## Example
58
81
 
@@ -758,6 +781,14 @@ A peripheral was disconnected.
758
781
  - `peripheral` - `String` - the id of the peripheral
759
782
  - `status` - `Number` - [Android only] disconnect [`reasons`](<https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onConnectionStateChange(android.bluetooth.BluetoothGatt,%20int,%20int)>)
760
783
 
784
+ ### BleManagerPeripheralDidBond
785
+
786
+ A bond with a peripheral was established
787
+
788
+ **Arguments**
789
+
790
+ Object with information about the device
791
+
761
792
  ### BleManagerCentralManagerWillRestoreState [iOS only]
762
793
 
763
794
  This is fired when [`centralManager:WillRestoreState:`](https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518819-centralmanager) is called (app relaunched in the background to handle a bluetooth event).
@@ -193,7 +193,7 @@ class BleManager extends ReactContextBaseJavaModule {
193
193
  for (Iterator<Map.Entry<String, Peripheral>> iterator = peripherals.entrySet().iterator(); iterator
194
194
  .hasNext(); ) {
195
195
  Map.Entry<String, Peripheral> entry = iterator.next();
196
- if (!entry.getValue().isConnected()) {
196
+ if (!(entry.getValue().isConnected() || entry.getValue().isConnecting())) {
197
197
  iterator.remove();
198
198
  }
199
199
  }
@@ -33,6 +33,8 @@ import java.util.Arrays;
33
33
  import java.util.Iterator;
34
34
  import java.util.List;
35
35
  import java.util.UUID;
36
+ import java.util.Queue;
37
+ import java.util.concurrent.ConcurrentLinkedQueue;
36
38
 
37
39
  import static android.os.Build.VERSION_CODES.LOLLIPOP;
38
40
  import static com.facebook.react.common.ReactConstants.TAG;
@@ -43,12 +45,15 @@ import static com.facebook.react.common.ReactConstants.TAG;
43
45
  public class Peripheral extends BluetoothGattCallback {
44
46
 
45
47
  private static final String CHARACTERISTIC_NOTIFICATION_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
48
+ public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5;
49
+ public static final int GATT_AUTH_FAIL = 137;
46
50
 
47
51
  private final BluetoothDevice device;
48
52
  private final Map<String, NotifyBufferContainer> bufferedCharacteristics;
49
53
  protected byte[] advertisingDataBytes = new byte[0];
50
54
  protected int advertisingRSSI;
51
55
  private boolean connected = false;
56
+ private boolean connecting = false;
52
57
  private ReactContext reactContext;
53
58
 
54
59
  private BluetoothGatt gatt;
@@ -61,6 +66,11 @@ public class Peripheral extends BluetoothGattCallback {
61
66
  private Callback registerNotifyCallback;
62
67
  private Callback requestMTUCallback;
63
68
 
69
+ private final Queue<Runnable> commandQueue = new ConcurrentLinkedQueue<>();
70
+ private final Handler mainHandler = new Handler(Looper.getMainLooper());
71
+ private Runnable discoverServicesRunnable;
72
+ private boolean commandQueueBusy = false;
73
+
64
74
  private List<byte[]> writeQueue = new ArrayList<>();
65
75
 
66
76
  public Peripheral(BluetoothDevice device, int advertisingRSSI, byte[] scanRecord, ReactContext reactContext) {
@@ -95,6 +105,7 @@ public class Peripheral extends BluetoothGattCallback {
95
105
  if (!connected) {
96
106
  BluetoothDevice device = getDevice();
97
107
  this.connectCallback = callback;
108
+ this.connecting = true;
98
109
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
99
110
  Log.d(BleManager.LOG_TAG, " Is Or Greater than M $mBluetoothDevice");
100
111
  gatt = device.connectGatt(activity, false, this, BluetoothDevice.TRANSPORT_LE);
@@ -127,6 +138,9 @@ public class Peripheral extends BluetoothGattCallback {
127
138
  connectCallback = null;
128
139
  connected = false;
129
140
  clearBuffers();
141
+ commandQueue.clear();
142
+ commandQueueBusy = false;
143
+
130
144
  if (gatt != null) {
131
145
  try {
132
146
  gatt.disconnect();
@@ -240,6 +254,10 @@ public class Peripheral extends BluetoothGattCallback {
240
254
  return connected;
241
255
  }
242
256
 
257
+ public boolean isConnecting() {
258
+ return connecting;
259
+ }
260
+
243
261
  public BluetoothDevice getDevice() {
244
262
  return device;
245
263
  }
@@ -272,11 +290,28 @@ public class Peripheral extends BluetoothGattCallback {
272
290
 
273
291
  if (status != BluetoothGatt.GATT_SUCCESS) {
274
292
  gatt.close();
293
+ // change the state to ensure the connection is teared down properly in the code below
294
+ newState = BluetoothProfile.STATE_DISCONNECTED;
275
295
  }
276
296
 
297
+ connecting = false;
277
298
  if (newState == BluetoothProfile.STATE_CONNECTED) {
278
299
  connected = true;
279
300
 
301
+ discoverServicesRunnable = new Runnable() {
302
+ @Override
303
+ public void run() {
304
+ try {
305
+ gatt.discoverServices();
306
+ } catch (NullPointerException e) {
307
+ Log.d(BleManager.LOG_TAG, "onConnectionStateChange connected but gatt of Run method was null");
308
+ }
309
+ discoverServicesRunnable = null;
310
+ }
311
+ };
312
+
313
+ mainHandler.post(discoverServicesRunnable);
314
+
280
315
  sendConnectionEvent(device, "BleManagerConnectPeripheral", status);
281
316
 
282
317
  if (connectCallback != null) {
@@ -287,9 +322,11 @@ public class Peripheral extends BluetoothGattCallback {
287
322
 
288
323
  } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
289
324
 
290
- this.disconnect(true);
325
+ if (discoverServicesRunnable != null) {
326
+ mainHandler.removeCallbacks(discoverServicesRunnable);
327
+ discoverServicesRunnable = null;
328
+ }
291
329
 
292
- sendConnectionEvent(device, "BleManagerDisconnectPeripheral", status);
293
330
  List<Callback> callbacks = Arrays.asList(writeCallback, retrieveServicesCallback, readRSSICallback,
294
331
  readCallback, registerNotifyCallback, requestMTUCallback);
295
332
  for (Callback currentCallback : callbacks) {
@@ -308,6 +345,10 @@ public class Peripheral extends BluetoothGattCallback {
308
345
  readRSSICallback = null;
309
346
  registerNotifyCallback = null;
310
347
  requestMTUCallback = null;
348
+ commandQueue.clear();
349
+ commandQueueBusy = false;
350
+
351
+ this.disconnect(true);
311
352
  }
312
353
 
313
354
  }
@@ -367,54 +408,60 @@ public class Peripheral extends BluetoothGattCallback {
367
408
  @Override
368
409
  public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
369
410
  super.onCharacteristicRead(gatt, characteristic, status);
370
- Log.d(BleManager.LOG_TAG, "onCharacteristicRead " + characteristic);
371
411
 
372
- if (readCallback != null) {
373
-
374
- if (status == BluetoothGatt.GATT_SUCCESS) {
375
- byte[] dataValue = characteristic.getValue();
376
-
377
- if (readCallback != null) {
378
- readCallback.invoke(null, BleManager.bytesToWritableArray(dataValue));
379
- }
380
- } else {
381
- readCallback.invoke("Error reading " + characteristic.getUuid() + " status=" + status, null);
412
+ if (status != BluetoothGatt.GATT_SUCCESS) {
413
+ if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
414
+ Log.d(BleManager.LOG_TAG, "Read needs bonding");
415
+ // *not* doing completedCommand()
416
+ return;
382
417
  }
383
-
418
+ sendBackrError(readCallback, "Error reading " + characteristic.getUuid() + " status=" + status);
419
+ } else if (readCallback != null) {
420
+ final byte[] dataValue = copyOf(characteristic.getValue());
421
+ final Callback callback = readCallback;
384
422
  readCallback = null;
385
-
423
+ mainHandler.post(new Runnable() {
424
+ @Override
425
+ public void run() {
426
+ callback.invoke(null, BleManager.bytesToWritableArray(dataValue));
427
+ }
428
+ });
386
429
  }
430
+
431
+ completedCommand();
387
432
  }
388
433
 
389
434
  @Override
390
435
  public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
391
436
  super.onCharacteristicWrite(gatt, characteristic, status);
392
437
 
393
- if (writeCallback != null) {
394
-
395
- if (writeQueue.size() > 0) {
396
- byte[] data = writeQueue.get(0);
397
- writeQueue.remove(0);
398
- doWrite(characteristic, data);
399
- } else {
400
-
401
- if (status == BluetoothGatt.GATT_SUCCESS) {
402
- writeCallback.invoke();
403
- } else {
404
- Log.e(BleManager.LOG_TAG, "Error onCharacteristicWrite:" + status);
405
- writeCallback.invoke("Error writing status: " + status);
406
- }
407
-
408
- writeCallback = null;
438
+ if (writeQueue.size() > 0) {
439
+ byte[] data = writeQueue.get(0);
440
+ writeQueue.remove(0);
441
+ doWrite(characteristic, data, writeCallback);
442
+ } else if (status != BluetoothGatt.GATT_SUCCESS) {
443
+ if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
444
+ Log.d(BleManager.LOG_TAG, "Write needs bonding");
445
+ // *not* doing completedCommand()
446
+ return;
409
447
  }
410
- } else {
411
- Log.e(BleManager.LOG_TAG, "No callback on write");
448
+ sendBackrError(writeCallback, "Error writing " + characteristic.getUuid() + " status=" + status);
449
+ } else if (writeCallback != null) {
450
+ final Callback callback = writeCallback;
451
+ mainHandler.post(new Runnable() {
452
+ @Override
453
+ public void run() {
454
+ callback.invoke();
455
+ }
456
+ });
457
+ writeCallback = null;
412
458
  }
459
+
460
+ completedCommand();
413
461
  }
414
462
 
415
463
  @Override
416
464
  public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
417
- super.onDescriptorWrite(gatt, descriptor, status);
418
465
  if (registerNotifyCallback != null) {
419
466
  if (status == BluetoothGatt.GATT_SUCCESS) {
420
467
  registerNotifyCallback.invoke();
@@ -428,6 +475,8 @@ public class Peripheral extends BluetoothGattCallback {
428
475
  } else {
429
476
  Log.e(BleManager.LOG_TAG, "onDescriptorWrite with no callback");
430
477
  }
478
+
479
+ completedCommand();
431
480
  }
432
481
 
433
482
  @Override
@@ -454,85 +503,90 @@ public class Peripheral extends BluetoothGattCallback {
454
503
  entry.getValue().resetBuffer();
455
504
  }
456
505
 
457
- private void setNotify(UUID serviceUUID, UUID characteristicUUID, Boolean notify, Integer buffer,
458
- Callback callback) {
459
- if (!isConnected()) {
506
+ private void setNotify(UUID serviceUUID, UUID characteristicUUID, final Boolean notify, Callback callback) {
507
+ if (! isConnected() || gatt == null) {
460
508
  callback.invoke("Device is not connected", null);
461
509
  return;
462
510
  }
463
- Log.d(BleManager.LOG_TAG, "setNotify");
464
511
 
465
- if (gatt == null) {
466
- callback.invoke("BluetoothGatt is null");
512
+ BluetoothGattService service = gatt.getService(serviceUUID);
513
+ final BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
514
+
515
+ if (characteristic == null) {
516
+ callback.invoke("Characteristic " + characteristicUUID + " not found");
467
517
  return;
468
518
  }
469
- BluetoothGattService service = gatt.getService(serviceUUID);
470
- BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
471
519
 
472
- if (characteristic != null) {
473
- if (gatt.setCharacteristicNotification(characteristic, notify)) {
520
+ if (! gatt.setCharacteristicNotification(characteristic, notify)) {
521
+ callback.invoke("Failed to register notification for " + characteristicUUID);
522
+ return;
523
+ }
474
524
 
475
- if (buffer > 1) {
476
- Log.d(BleManager.LOG_TAG, "Characteristic buffering " + characteristicUUID + " count:" + buffer);
477
- String key = this.bufferedCharacteristicsKey(serviceUUID.toString(), characteristicUUID.toString());
478
- this.bufferedCharacteristics.put(key, new NotifyBufferContainer(key, buffer));
479
- }
525
+ final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUIDHelper.uuidFromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
526
+ if (descriptor == null) {
527
+ callback.invoke("Set notification failed for " + characteristicUUID);
528
+ return;
529
+ }
530
+
531
+ // Prefer notify over indicate
532
+ byte[] value;
533
+ if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
534
+ Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
535
+ value = notify ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
536
+ } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
537
+ Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
538
+ value = notify ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
539
+ } else {
540
+ String msg = "Characteristic " + characteristicUUID + " does not have NOTIFY or INDICATE property set";
541
+ Log.d(BleManager.LOG_TAG, msg);
542
+ callback.invoke(msg);
543
+ return;
544
+ }
545
+ final byte[] finalValue = notify ? value : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
546
+ final Callback finalCallback = callback;
480
547
 
481
- BluetoothGattDescriptor descriptor = characteristic
482
- .getDescriptor(UUIDHelper.uuidFromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
483
- if (descriptor != null) {
484
-
485
- // Prefer notify over indicate
486
- if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
487
- Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
488
- descriptor.setValue(notify ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
489
- : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
490
- } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
491
- Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
492
- descriptor.setValue(notify ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
493
- : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
494
- } else {
495
- Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID
496
- + " does not have NOTIFY or INDICATE property set");
548
+ boolean result = commandQueue.add(new Runnable() {
549
+ @Override
550
+ public void run() {
551
+ if (! isConnected()) {
552
+ completedCommand();
553
+ return;
497
554
  }
498
555
 
556
+ boolean result = false;
499
557
  try {
500
- registerNotifyCallback = callback;
501
- if (gatt.writeDescriptor(descriptor)) {
502
- Log.d(BleManager.LOG_TAG, "setNotify complete");
503
- } else {
504
- registerNotifyCallback = null;
505
- callback.invoke(
506
- "Failed to set client characteristic notification for " + characteristicUUID);
507
- }
508
- } catch (Exception e) {
509
- Log.d(BleManager.LOG_TAG, "Error on setNotify", e);
510
- callback.invoke("Failed to set client characteristic notification for " + characteristicUUID
511
- + ", error: " + e.getMessage());
558
+ result = gatt.setCharacteristicNotification(characteristic, notify);
559
+ // Then write to descriptor
560
+ descriptor.setValue(finalValue);
561
+ registerNotifyCallback = finalCallback;
562
+ result = gatt.writeDescriptor(descriptor);
563
+ } catch(Exception e) {
564
+ Log.d(BleManager.LOG_TAG, "Exception in setNotify", e);
512
565
  }
513
566
 
514
- } else {
515
- callback.invoke("Set notification failed for " + characteristicUUID);
567
+ if (! result) {
568
+ sendBackrError(registerNotifyCallback, "writeDescriptor failed for descriptor: " + descriptor.getUuid());
569
+ registerNotifyCallback = null;
570
+ completedCommand();
571
+ }
516
572
  }
573
+ });
517
574
 
518
- } else {
519
- callback.invoke("Failed to register notification for " + characteristicUUID);
520
- }
521
-
575
+ if (result) {
576
+ nextCommand();
522
577
  } else {
523
- callback.invoke("Characteristic " + characteristicUUID + " not found");
578
+ Log.e(BleManager.LOG_TAG, "Could not enqueue setNotify command");
524
579
  }
525
-
526
580
  }
527
581
 
528
582
  public void registerNotify(UUID serviceUUID, UUID characteristicUUID, Integer buffer, Callback callback) {
529
583
  Log.d(BleManager.LOG_TAG, "registerNotify");
530
- this.setNotify(serviceUUID, characteristicUUID, true, buffer, callback);
584
+ this.setNotify(serviceUUID, characteristicUUID, true, callback);
531
585
  }
532
586
 
533
587
  public void removeNotify(UUID serviceUUID, UUID characteristicUUID, Callback callback) {
534
588
  Log.d(BleManager.LOG_TAG, "removeNotify");
535
- this.setNotify(serviceUUID, characteristicUUID, false, 1, callback);
589
+ this.setNotify(serviceUUID, characteristicUUID, false, callback);
536
590
  }
537
591
 
538
592
  // Some devices reuse UUIDs across characteristics, so we can't use
@@ -572,30 +626,100 @@ public class Peripheral extends BluetoothGattCallback {
572
626
 
573
627
  public void read(UUID serviceUUID, UUID characteristicUUID, Callback callback) {
574
628
 
575
- if (!isConnected()) {
629
+ if (!isConnected() || gatt == null) {
576
630
  callback.invoke("Device is not connected", null);
577
631
  return;
578
632
  }
579
- if (gatt == null) {
580
- callback.invoke("BluetoothGatt is null", null);
581
- return;
582
- }
583
633
 
584
634
  BluetoothGattService service = gatt.getService(serviceUUID);
585
- BluetoothGattCharacteristic characteristic = findReadableCharacteristic(service, characteristicUUID);
635
+ final BluetoothGattCharacteristic characteristic = findReadableCharacteristic(service, characteristicUUID);
586
636
 
587
637
  if (characteristic == null) {
588
638
  callback.invoke("Characteristic " + characteristicUUID + " not found.", null);
639
+ return;
640
+ }
641
+
642
+ final Callback reactcb = callback;
643
+ boolean result = commandQueue.add(new Runnable() {
644
+ @Override
645
+ public void run() {
646
+ readCallback = reactcb;
647
+ if (! gatt.readCharacteristic(characteristic)) {
648
+ readCallback = null;
649
+ sendBackrError(reactcb, "Read failed");
650
+ completedCommand();
651
+ }
652
+ }
653
+ });
654
+
655
+ if (result) {
656
+ nextCommand();
589
657
  } else {
590
- readCallback = callback;
591
- if (!gatt.readCharacteristic(characteristic)) {
592
- readCallback = null;
593
- callback.invoke("Read failed", null);
658
+ Log.d(BleManager.LOG_TAG, "Could not queue read characteristic command");
659
+ }
660
+ }
661
+
662
+ private void sendBackrError(final Callback callback, final String message) {
663
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
664
+ @Override
665
+ public void run() {
666
+ callback.invoke(message, null);
667
+ }
668
+ });
669
+ }
670
+
671
+ private byte[] copyOf(byte[] source) {
672
+ if (source == null) return new byte[0];
673
+ final int sourceLength = source.length;
674
+ final byte[] copy = new byte[sourceLength];
675
+ System.arraycopy(source, 0, copy, 0, sourceLength);
676
+ return copy;
677
+ }
678
+
679
+ private void completedCommand() {
680
+ commandQueue.poll();
681
+ commandQueueBusy = false;
682
+ nextCommand();
683
+ }
684
+
685
+ private void nextCommand() {
686
+ synchronized (this) {
687
+ if (commandQueueBusy) {
688
+ Log.d(BleManager.LOG_TAG, "Command queue busy");
689
+ return;
690
+ }
691
+
692
+ final Runnable nextCommand = commandQueue.peek();
693
+ if (nextCommand == null) {
694
+ Log.d(BleManager.LOG_TAG, "Command queue empty");
695
+ return;
696
+ }
697
+
698
+ // Check if we still have a valid gatt object
699
+ if (gatt == null) {
700
+ Log.d(BleManager.LOG_TAG, "Error, gatt is null");
701
+ commandQueue.clear();
702
+ commandQueueBusy = false;
703
+ return;
594
704
  }
705
+
706
+ // Execute the next command in the queue
707
+ commandQueueBusy = true;
708
+ mainHandler.post(new Runnable() {
709
+ @Override
710
+ public void run() {
711
+ try {
712
+ nextCommand.run();
713
+ } catch (Exception ex) {
714
+ Log.d(BleManager.LOG_TAG, "Error, command exception");
715
+ completedCommand();
716
+ }
717
+ }
718
+ });
595
719
  }
596
720
  }
597
721
 
598
- public void readRSSI(Callback callback) {
722
+ public void readRSSI(final Callback callback) {
599
723
  if (!isConnected()) {
600
724
  callback.invoke("Device is not connected", null);
601
725
  return;
@@ -605,11 +729,26 @@ public class Peripheral extends BluetoothGattCallback {
605
729
  return;
606
730
  }
607
731
 
608
- readRSSICallback = callback;
732
+ final boolean result = commandQueue.add(new Runnable() {
733
+ @Override
734
+ public void run() {
735
+ if (isConnected()) {
736
+ readRSSICallback = callback;
737
+ if (! gatt.readRemoteRssi()) {
738
+ readCallback = null;
739
+ sendBackrError(callback, "Read RSSI failed");
740
+ completedCommand();
741
+ }
742
+ } else {
743
+ completedCommand();
744
+ }
745
+ }
746
+ });
609
747
 
610
- if (!gatt.readRemoteRssi()) {
611
- readCallback = null;
612
- callback.invoke("Read RSSI failed", null);
748
+ if (result) {
749
+ nextCommand();
750
+ } else {
751
+ Log.d(BleManager.LOG_TAG, "Could not queue readRemoteRssi command");
613
752
  }
614
753
  }
615
754
 
@@ -667,112 +806,109 @@ public class Peripheral extends BluetoothGattCallback {
667
806
  return null;
668
807
  }
669
808
 
670
- public boolean doWrite(BluetoothGattCharacteristic characteristic, byte[] data) {
671
- characteristic.setValue(data);
809
+ public boolean doWrite(final BluetoothGattCharacteristic characteristic, byte[] data, final Callback callback) {
810
+ final byte[] copyOfData = copyOf(data);
811
+ boolean result = commandQueue.add(new Runnable() {
812
+ @Override
813
+ public void run() {
814
+ characteristic.setValue(copyOfData);
815
+ if (characteristic.getWriteType() == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
816
+ writeCallback = callback;
817
+ else
818
+ writeCallback = null;
819
+ if (!gatt.writeCharacteristic(characteristic)) {
820
+ sendBackrError(writeCallback, "Write failed");
821
+ writeCallback = null;
822
+ completedCommand();
823
+ }
824
+ }
825
+ });
672
826
 
673
- if (!gatt.writeCharacteristic(characteristic)) {
674
- Log.d(BleManager.LOG_TAG, "Error on doWrite");
675
- return false;
827
+ if (result) {
828
+ nextCommand();
829
+ } else {
830
+ Log.d(BleManager.LOG_TAG, "Could not queue write characteristic command");
676
831
  }
677
- return true;
832
+
833
+ return result;
678
834
  }
679
835
 
680
- public void write(UUID serviceUUID, UUID characteristicUUID, byte[] data, Integer maxByteSize,
681
- Integer queueSleepTime, Callback callback, int writeType) {
682
- if (!isConnected()) {
836
+ public void write(UUID serviceUUID, UUID characteristicUUID, byte[] data, Integer maxByteSize, Integer queueSleepTime, Callback callback, int writeType) {
837
+ if (!isConnected() || gatt == null) {
683
838
  callback.invoke("Device is not connected", null);
684
839
  return;
685
840
  }
686
- if (gatt == null) {
687
- callback.invoke("BluetoothGatt is null");
688
- } else {
689
- BluetoothGattService service = gatt.getService(serviceUUID);
690
- BluetoothGattCharacteristic characteristic = findWritableCharacteristic(service, characteristicUUID,
691
- writeType);
692
841
 
693
- if (characteristic == null) {
694
- callback.invoke("Characteristic " + characteristicUUID + " not found.");
695
- } else {
696
- characteristic.setWriteType(writeType);
842
+ BluetoothGattService service = gatt.getService(serviceUUID);
843
+ BluetoothGattCharacteristic characteristic = findWritableCharacteristic(service, characteristicUUID, writeType);
697
844
 
698
- if (writeQueue.size() > 0) {
699
- callback.invoke("You have already an queued message");
700
- return;
701
- }
845
+ if (characteristic == null) {
846
+ callback.invoke("Characteristic " + characteristicUUID + " not found.");
847
+ return;
848
+ }
702
849
 
703
- if (writeCallback != null) {
704
- callback.invoke("You're already writing");
705
- return;
706
- }
850
+ characteristic.setWriteType(writeType);
707
851
 
708
- if (writeQueue.size() == 0 && writeCallback == null) {
852
+ if (data.length <= maxByteSize) {
853
+ if (! doWrite(characteristic, data, callback)) {
854
+ callback.invoke("Write failed");
855
+ writeCallback = null;
856
+ } else {
857
+ if (BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE == writeType) {
858
+ callback.invoke();
859
+ }
860
+ }
861
+ } else {
862
+ int dataLength = data.length;
863
+ int count = 0;
864
+ byte[] firstMessage = null;
865
+ List<byte[]> splittedMessage = new ArrayList<>();
866
+
867
+ while (count < dataLength && (dataLength - count > maxByteSize)) {
868
+ if (count == 0) {
869
+ firstMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
870
+ } else {
871
+ byte[] splitMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
872
+ splittedMessage.add(splitMessage);
873
+ }
874
+ count += maxByteSize;
875
+ }
876
+ if (count < dataLength) {
877
+ // Other bytes in queue
878
+ byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
879
+ splittedMessage.add(splitMessage);
880
+ }
709
881
 
710
- if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
711
- writeCallback = callback;
882
+ if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
883
+ writeQueue.addAll(splittedMessage);
884
+ if (! doWrite(characteristic, firstMessage, callback)) {
885
+ writeQueue.clear();
886
+ writeCallback = null;
887
+ callback.invoke("Write failed");
888
+ }
889
+ } else {
890
+ try {
891
+ boolean writeError = false;
892
+ if (! doWrite(characteristic, firstMessage, callback)) {
893
+ writeError = true;
894
+ callback.invoke("Write failed");
712
895
  }
713
-
714
- if (data.length > maxByteSize) {
715
- int dataLength = data.length;
716
- int count = 0;
717
- byte[] firstMessage = null;
718
- List<byte[]> splittedMessage = new ArrayList<>();
719
-
720
- while (count < dataLength && (dataLength - count > maxByteSize)) {
721
- if (count == 0) {
722
- firstMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
723
- } else {
724
- byte[] splitMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
725
- splittedMessage.add(splitMessage);
726
- }
727
- count += maxByteSize;
728
- }
729
- if (count < dataLength) {
730
- // Other bytes in queue
731
- byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
732
- splittedMessage.add(splitMessage);
733
- }
734
-
735
- if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
736
- writeQueue.addAll(splittedMessage);
737
- if (!doWrite(characteristic, firstMessage)) {
738
- writeQueue.clear();
739
- writeCallback = null;
896
+ if (! writeError) {
897
+ Thread.sleep(queueSleepTime);
898
+ for (byte[] message : splittedMessage) {
899
+ if (! doWrite(characteristic, message, callback)) {
900
+ writeError = true;
740
901
  callback.invoke("Write failed");
902
+ break;
741
903
  }
742
- } else {
743
- try {
744
- boolean writeError = false;
745
- if (!doWrite(characteristic, firstMessage)) {
746
- writeError = true;
747
- callback.invoke("Write failed");
748
- }
749
- if (!writeError) {
750
- Thread.sleep(queueSleepTime);
751
- for (byte[] message : splittedMessage) {
752
- if (!doWrite(characteristic, message)) {
753
- writeError = true;
754
- callback.invoke("Write failed");
755
- break;
756
- }
757
- Thread.sleep(queueSleepTime);
758
- }
759
- if (!writeError) {
760
- callback.invoke();
761
- }
762
- }
763
- } catch (InterruptedException e) {
764
- callback.invoke("Error during writing");
765
- }
904
+ Thread.sleep(queueSleepTime);
766
905
  }
767
- } else if (doWrite(characteristic, data)) {
768
- Log.d(BleManager.LOG_TAG, "Write completed");
769
- if (BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE == writeType) {
906
+ if (! writeError) {
770
907
  callback.invoke();
771
908
  }
772
- } else {
773
- callback.invoke("Write failed");
774
- writeCallback = null;
775
909
  }
910
+ } catch (InterruptedException e) {
911
+ callback.invoke("Error during writing");
776
912
  }
777
913
  }
778
914
  }
package/ios/BleManager.m CHANGED
@@ -720,14 +720,6 @@ RCT_EXPORT_METHOD(requestMTU:(NSString *)deviceUUID mtu:(NSInteger)mtu callback:
720
720
  callback(@[@"Not supported"]);
721
721
  }
722
722
 
723
- RCT_EXPORT_METHOD(addListener : (NSString *)eventName) {
724
- // Keep: Required for RN built in Event Emitter Calls.
725
- }
726
-
727
- RCT_EXPORT_METHOD(removeListeners : (NSInteger)count) {
728
- // Keep: Required for RN built in Event Emitter Calls.
729
- }
730
-
731
723
  - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
732
724
  NSLog(@"didWrite");
733
725
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-ble-manager",
3
- "version": "7.6.2",
3
+ "version": "8.0.2",
4
4
  "description": "A BLE module for react native.",
5
5
  "main": "BleManager",
6
6
  "types": "./index.d.ts",
package/ISSUE_TEMPLATE.md DELETED
@@ -1,33 +0,0 @@
1
- ### Before open an issue
2
- - Check the closed issues, your question maybe is not new.
3
- - We can't debug your physical devices.
4
- - If the library is not working 99% you did something wrong, in the code or in the phone permissions.
5
-
6
- ### Version
7
-
8
- Tell us which versions you are using:
9
-
10
- - react-native-ble-manager v?.?.?
11
- - react-native v?.?
12
- - iOS/Android v.?
13
-
14
- ### Expected behaviour
15
-
16
- Tell us what should happen
17
-
18
- ### Actual behaviour
19
-
20
- Tell us what happens instead
21
-
22
- ### Steps to reproduce
23
-
24
- 1.
25
- 2.
26
- 3.
27
-
28
- ### Stack trace and console log
29
-
30
- Hint: it would help a lot if you enable the debugger ("Pause on exceptions" in the "Source" panel of Chrome dev tools) and spot the place where the error is thrown
31
-
32
- ```
33
- ```