react-native-ble-manager 8.7.0 → 9.0.0

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.
Files changed (56) hide show
  1. package/README.md +96 -47
  2. package/android/.gradle/7.4/checksums/checksums.lock +0 -0
  3. package/android/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock +0 -0
  4. package/android/.gradle/7.4/dependencies-accessors/gc.properties +0 -0
  5. package/android/.gradle/7.4/executionHistory/executionHistory.bin +0 -0
  6. package/android/.gradle/7.4/executionHistory/executionHistory.lock +0 -0
  7. package/android/.gradle/7.4/fileChanges/last-build.bin +0 -0
  8. package/android/.gradle/7.4/fileHashes/fileHashes.bin +0 -0
  9. package/android/.gradle/7.4/fileHashes/fileHashes.lock +0 -0
  10. package/android/.gradle/7.4/fileHashes/resourceHashesCache.bin +0 -0
  11. package/android/.gradle/7.4/gc.properties +0 -0
  12. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  13. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  14. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  15. package/android/.gradle/file-system.probe +0 -0
  16. package/android/.gradle/vcs-1/gc.properties +0 -0
  17. package/android/.idea/.name +1 -0
  18. package/android/.idea/compiler.xml +6 -0
  19. package/android/.idea/gradle.xml +18 -0
  20. package/android/.idea/jarRepositories.xml +40 -0
  21. package/android/.idea/misc.xml +10 -0
  22. package/android/.idea/vcs.xml +6 -0
  23. package/android/build.gradle +17 -17
  24. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  25. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  26. package/android/gradle.properties +3 -1
  27. package/android/gradlew +234 -0
  28. package/android/gradlew.bat +89 -0
  29. package/android/local.properties +8 -0
  30. package/android/src/main/java/it/innove/BleManager.java +33 -17
  31. package/android/src/main/java/it/innove/LegacyScanManager.java +33 -28
  32. package/android/src/main/java/it/innove/LollipopPeripheral.java +66 -65
  33. package/android/src/main/java/it/innove/LollipopScanManager.java +52 -25
  34. package/android/src/main/java/it/innove/Peripheral.java +889 -837
  35. package/android/src/main/java/it/innove/ScanManager.java +24 -23
  36. package/dist/cjs/index.d.ts +147 -0
  37. package/dist/cjs/index.js +487 -0
  38. package/dist/cjs/index.js.map +1 -0
  39. package/dist/cjs/types.d.ts +311 -0
  40. package/dist/cjs/types.js +110 -0
  41. package/dist/cjs/types.js.map +1 -0
  42. package/dist/esm/index.d.ts +147 -0
  43. package/dist/esm/index.js +471 -0
  44. package/dist/esm/index.js.map +1 -0
  45. package/dist/esm/types.d.ts +311 -0
  46. package/dist/esm/types.js +107 -0
  47. package/dist/esm/types.js.map +1 -0
  48. package/ios/BleManager.m +87 -110
  49. package/ios/BleManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  50. package/package.json +31 -4
  51. package/src/index.ts +569 -0
  52. package/src/types.ts +336 -0
  53. package/.github/FUNDING.yml +0 -3
  54. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  55. package/BleManager.js +0 -427
  56. package/index.d.ts +0 -168
@@ -1,5 +1,8 @@
1
1
  package it.innove;
2
2
 
3
+ import static android.os.Build.VERSION_CODES.LOLLIPOP;
4
+ import static com.facebook.react.common.ReactConstants.TAG;
5
+
3
6
  import android.app.Activity;
4
7
  import android.bluetooth.BluetoothDevice;
5
8
  import android.bluetooth.BluetoothGatt;
@@ -12,10 +15,11 @@ import android.content.Context;
12
15
  import android.os.Build;
13
16
  import android.os.Handler;
14
17
  import android.os.Looper;
15
- import androidx.annotation.Nullable;
16
18
  import android.util.Base64;
17
19
  import android.util.Log;
18
20
 
21
+ import androidx.annotation.Nullable;
22
+
19
23
  import com.facebook.react.bridge.Arguments;
20
24
  import com.facebook.react.bridge.Callback;
21
25
  import com.facebook.react.bridge.ReactContext;
@@ -27,441 +31,469 @@ import org.json.JSONException;
27
31
 
28
32
  import java.lang.reflect.Method;
29
33
  import java.util.ArrayList;
30
- import java.util.Map;
31
34
  import java.util.Arrays;
32
35
  import java.util.Iterator;
36
+ import java.util.LinkedList;
33
37
  import java.util.List;
34
- import java.util.UUID;
38
+ import java.util.Map;
35
39
  import java.util.Queue;
40
+ import java.util.UUID;
36
41
  import java.util.concurrent.ConcurrentHashMap;
37
42
  import java.util.concurrent.ConcurrentLinkedQueue;
38
43
 
39
- import static android.os.Build.VERSION_CODES.LOLLIPOP;
40
- import static com.facebook.react.common.ReactConstants.TAG;
41
-
42
44
  /**
43
45
  * Peripheral wraps the BluetoothDevice and provides methods to convert to JSON.
44
46
  */
45
47
  public class Peripheral extends BluetoothGattCallback {
46
48
 
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;
50
-
51
- private final BluetoothDevice device;
52
- private final Map<String, NotifyBufferContainer> bufferedCharacteristics;
53
- protected volatile byte[] advertisingDataBytes = new byte[0];
54
- protected volatile int advertisingRSSI;
55
- private volatile boolean connected = false;
56
- private volatile boolean connecting = false;
57
- private ReactContext reactContext;
58
-
59
- private BluetoothGatt gatt;
60
-
61
- private Callback connectCallback;
62
- private Callback retrieveServicesCallback;
63
- private Callback readCallback;
64
- private Callback readRSSICallback;
65
- private Callback writeCallback;
66
- private Callback registerNotifyCallback;
67
- private Callback requestMTUCallback;
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
-
74
- private List<byte[]> writeQueue = new ArrayList<>();
75
-
76
- public Peripheral(BluetoothDevice device, int advertisingRSSI, byte[] scanRecord, ReactContext reactContext) {
77
- this.device = device;
78
- this.bufferedCharacteristics = new ConcurrentHashMap<String, NotifyBufferContainer>();
79
- this.advertisingRSSI = advertisingRSSI;
80
- this.advertisingDataBytes = scanRecord;
81
- this.reactContext = reactContext;
82
- }
83
-
84
- public Peripheral(BluetoothDevice device, ReactContext reactContext) {
85
- this.device = device;
86
- this.bufferedCharacteristics = new ConcurrentHashMap<String, NotifyBufferContainer>();
87
- this.reactContext = reactContext;
88
- }
89
-
90
- private void sendEvent(String eventName, @Nullable WritableMap params) {
91
- synchronized (reactContext) {
92
- reactContext.getJSModule(RCTNativeAppEventEmitter.class).emit(eventName, params);
93
- }
94
- }
95
-
96
- private void sendConnectionEvent(BluetoothDevice device, String eventName, int status) {
97
- WritableMap map = Arguments.createMap();
98
- map.putString("peripheral", device.getAddress());
99
- if (status != -1) {
100
- map.putInt("status", status);
101
- }
102
- sendEvent(eventName, map);
103
- Log.d(BleManager.LOG_TAG, "Peripheral event (" + eventName + "):" + device.getAddress());
104
- }
105
-
106
- public void connect(final Callback callback, Activity activity) {
107
- mainHandler.post(() -> {
108
- if (!connected) {
109
- BluetoothDevice device = getDevice();
110
- this.connectCallback = callback;
111
- this.connecting = true;
112
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
113
- Log.d(BleManager.LOG_TAG, " Is Or Greater than M $mBluetoothDevice");
114
- gatt = device.connectGatt(activity, false, this, BluetoothDevice.TRANSPORT_LE);
115
- } else {
116
- Log.d(BleManager.LOG_TAG, " Less than M");
117
- try {
118
- Log.d(BleManager.LOG_TAG, " Trying TRANPORT LE with reflection");
119
- Method m = device.getClass().getDeclaredMethod("connectGatt", Context.class, Boolean.class,
120
- BluetoothGattCallback.class, Integer.class);
121
- m.setAccessible(true);
122
- Integer transport = device.getClass().getDeclaredField("TRANSPORT_LE").getInt(null);
123
- gatt = (BluetoothGatt) m.invoke(device, activity, false, this, transport);
124
- } catch (Exception e) {
125
- e.printStackTrace();
126
- Log.d(TAG, " Catch to call normal connection");
127
- gatt = device.connectGatt(activity, false, this);
128
- }
129
- }
130
- } else {
131
- if (gatt != null) {
132
- callback.invoke();
133
- } else {
134
- callback.invoke("BluetoothGatt is null");
135
- }
136
- }
137
- });
138
- }
139
- // bt_btif : Register with GATT stack failed.
49
+ private static final String CHARACTERISTIC_NOTIFICATION_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
50
+ public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5;
51
+ public static final int GATT_AUTH_FAIL = 137;
52
+
53
+ private final BluetoothDevice device;
54
+ private final Map<String, NotifyBufferContainer> bufferedCharacteristics;
55
+ protected volatile byte[] advertisingDataBytes = new byte[0];
56
+ protected volatile int advertisingRSSI;
57
+ private volatile boolean connected = false;
58
+ private volatile boolean connecting = false;
59
+ private ReactContext reactContext;
60
+
61
+ private BluetoothGatt gatt;
62
+
63
+ private LinkedList<Callback> connectCallbacks = new LinkedList<>();
64
+ private LinkedList<Callback> retrieveServicesCallbacks = new LinkedList<>();
65
+ private LinkedList<Callback> readCallbacks = new LinkedList<>();
66
+ private LinkedList<Callback> readRSSICallbacks = new LinkedList<>();
67
+ private LinkedList<Callback> writeCallbacks = new LinkedList<>();
68
+ private LinkedList<Callback> registerNotifyCallbacks = new LinkedList<>();
69
+ private LinkedList<Callback> requestMTUCallbacks = new LinkedList<>();
70
+
71
+ private final Queue<Runnable> commandQueue = new ConcurrentLinkedQueue<>();
72
+ private final Handler mainHandler = new Handler(Looper.getMainLooper());
73
+ private Runnable discoverServicesRunnable;
74
+ private boolean commandQueueBusy = false;
75
+
76
+ private List<byte[]> writeQueue = new ArrayList<>();
77
+
78
+ public Peripheral(BluetoothDevice device, int advertisingRSSI, byte[] scanRecord, ReactContext reactContext) {
79
+ this.device = device;
80
+ this.bufferedCharacteristics = new ConcurrentHashMap<String, NotifyBufferContainer>();
81
+ this.advertisingRSSI = advertisingRSSI;
82
+ this.advertisingDataBytes = scanRecord;
83
+ this.reactContext = reactContext;
84
+ }
85
+
86
+ public Peripheral(BluetoothDevice device, ReactContext reactContext) {
87
+ this.device = device;
88
+ this.bufferedCharacteristics = new ConcurrentHashMap<String, NotifyBufferContainer>();
89
+ this.reactContext = reactContext;
90
+ }
91
+
92
+ private void sendEvent(String eventName, @Nullable WritableMap params) {
93
+ synchronized (reactContext) {
94
+ reactContext.getJSModule(RCTNativeAppEventEmitter.class).emit(eventName, params);
95
+ }
96
+ }
97
+
98
+ private void sendConnectionEvent(BluetoothDevice device, String eventName, int status) {
99
+ WritableMap map = Arguments.createMap();
100
+ map.putString("peripheral", device.getAddress());
101
+ if (status != -1) {
102
+ map.putInt("status", status);
103
+ }
104
+ sendEvent(eventName, map);
105
+ Log.d(BleManager.LOG_TAG, "Peripheral event (" + eventName + "):" + device.getAddress());
106
+ }
107
+
108
+ public void connect(final Callback callback, Activity activity) {
109
+ mainHandler.post(() -> {
110
+ if (!connected) {
111
+ BluetoothDevice device = getDevice();
112
+ this.connectCallbacks.addLast(callback);
113
+ this.connecting = true;
114
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
115
+ Log.d(BleManager.LOG_TAG, " Is Or Greater than M $mBluetoothDevice");
116
+ gatt = device.connectGatt(activity, false, this, BluetoothDevice.TRANSPORT_LE);
117
+ } else {
118
+ Log.d(BleManager.LOG_TAG, " Less than M");
119
+ try {
120
+ Log.d(BleManager.LOG_TAG, " Trying TRANPORT LE with reflection");
121
+ Method m = device.getClass().getDeclaredMethod("connectGatt", Context.class, Boolean.class,
122
+ BluetoothGattCallback.class, Integer.class);
123
+ m.setAccessible(true);
124
+ Integer transport = device.getClass().getDeclaredField("TRANSPORT_LE").getInt(null);
125
+ gatt = (BluetoothGatt) m.invoke(device, activity, false, this, transport);
126
+ } catch (Exception e) {
127
+ e.printStackTrace();
128
+ Log.d(TAG, " Catch to call normal connection");
129
+ gatt = device.connectGatt(activity, false, this);
130
+ }
131
+ }
132
+ } else {
133
+ if (gatt != null) {
134
+ callback.invoke();
135
+ } else {
136
+ callback.invoke("BluetoothGatt is null");
137
+ }
138
+ }
139
+ });
140
+ }
141
+ // bt_btif : Register with GATT stack failed.
140
142
 
141
143
  public void disconnect(final Callback callback, final boolean force) {
142
144
  mainHandler.post(() -> {
143
- connectCallback = null;
145
+ for (Callback connectCallback: connectCallbacks) {
146
+ connectCallback.invoke("Disconnect called before connect callback invoked");
147
+ }
148
+ connectCallbacks.clear();
144
149
  connected = false;
145
150
  clearBuffers();
146
151
  commandQueue.clear();
147
152
  commandQueueBusy = false;
148
153
 
149
- if (gatt != null) {
150
- try {
151
- gatt.disconnect();
152
- if (force) {
153
- gatt.close();
154
- gatt = null;
155
- sendConnectionEvent(device, "BleManagerDisconnectPeripheral", BluetoothGatt.GATT_SUCCESS);
156
- }
157
- Log.d(BleManager.LOG_TAG, "Disconnect");
158
- } catch (Exception e) {
159
- sendConnectionEvent(device, "BleManagerDisconnectPeripheral", BluetoothGatt.GATT_FAILURE);
160
- Log.d(BleManager.LOG_TAG, "Error on disconnect", e);
161
- }
162
- } else
163
- Log.d(BleManager.LOG_TAG, "GATT is null");
164
- if (callback != null)
165
- callback.invoke();
166
- });
167
- }
168
-
169
- public WritableMap asWritableMap() {
170
- WritableMap map = Arguments.createMap();
171
- WritableMap advertising = Arguments.createMap();
172
-
173
- try {
174
- map.putString("name", device.getName());
175
- map.putString("id", device.getAddress()); // mac address
176
- map.putInt("rssi", advertisingRSSI);
177
-
178
- String name = device.getName();
179
- if (name != null)
180
- advertising.putString("localName", name);
181
-
182
- advertising.putMap("manufacturerData", byteArrayToWritableMap(advertisingDataBytes));
183
-
184
- // No scanResult to access so we can't check if peripheral is connectable
185
- advertising.putBoolean("isConnectable", true);
186
-
187
- map.putMap("advertising", advertising);
188
- } catch (Exception e) { // this shouldn't happen
189
- e.printStackTrace();
190
- }
191
-
192
- return map;
193
- }
194
-
195
- public WritableMap asWritableMap(BluetoothGatt gatt) {
196
-
197
- WritableMap map = asWritableMap();
198
-
199
- WritableArray servicesArray = Arguments.createArray();
200
- WritableArray characteristicsArray = Arguments.createArray();
201
-
202
- if (connected && gatt != null) {
203
- for (Iterator<BluetoothGattService> it = gatt.getServices().iterator(); it.hasNext();) {
204
- BluetoothGattService service = it.next();
205
- WritableMap serviceMap = Arguments.createMap();
206
- serviceMap.putString("uuid", UUIDHelper.uuidToString(service.getUuid()));
207
-
208
- for (Iterator<BluetoothGattCharacteristic> itCharacteristic = service.getCharacteristics()
209
- .iterator(); itCharacteristic.hasNext();) {
210
- BluetoothGattCharacteristic characteristic = itCharacteristic.next();
211
- WritableMap characteristicsMap = Arguments.createMap();
212
-
213
- characteristicsMap.putString("service", UUIDHelper.uuidToString(service.getUuid()));
214
- characteristicsMap.putString("characteristic", UUIDHelper.uuidToString(characteristic.getUuid()));
215
-
216
- characteristicsMap.putMap("properties", Helper.decodeProperties(characteristic));
217
-
218
- if (characteristic.getPermissions() > 0) {
219
- characteristicsMap.putMap("permissions", Helper.decodePermissions(characteristic));
220
- }
221
-
222
- WritableArray descriptorsArray = Arguments.createArray();
223
-
224
- for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
225
- WritableMap descriptorMap = Arguments.createMap();
226
- descriptorMap.putString("uuid", UUIDHelper.uuidToString(descriptor.getUuid()));
227
- if (descriptor.getValue() != null) {
228
- descriptorMap.putString("value",
229
- Base64.encodeToString(descriptor.getValue(), Base64.NO_WRAP));
230
- } else {
231
- descriptorMap.putString("value", null);
232
- }
233
-
234
- if (descriptor.getPermissions() > 0) {
235
- descriptorMap.putMap("permissions", Helper.decodePermissions(descriptor));
236
- }
237
- descriptorsArray.pushMap(descriptorMap);
238
- }
239
- if (descriptorsArray.size() > 0) {
240
- characteristicsMap.putArray("descriptors", descriptorsArray);
241
- }
242
- characteristicsArray.pushMap(characteristicsMap);
243
- }
244
- servicesArray.pushMap(serviceMap);
245
- }
246
- map.putArray("services", servicesArray);
247
- map.putArray("characteristics", characteristicsArray);
248
- }
249
-
250
- return map;
251
- }
252
-
253
- static WritableMap byteArrayToWritableMap(byte[] bytes) throws JSONException {
254
- WritableMap object = Arguments.createMap();
255
- object.putString("CDVType", "ArrayBuffer");
256
- object.putString("data", bytes != null ? Base64.encodeToString(bytes, Base64.NO_WRAP) : null);
257
- object.putArray("bytes", bytes != null ? BleManager.bytesToWritableArray(bytes) : null);
258
- return object;
259
- }
260
-
261
- public boolean isConnected() {
262
- return connected;
263
- }
264
-
265
- public boolean isConnecting() {
266
- return connecting;
267
- }
268
-
269
- public BluetoothDevice getDevice() {
270
- return device;
271
- }
154
+ if (gatt != null) {
155
+ try {
156
+ gatt.disconnect();
157
+ if (force) {
158
+ gatt.close();
159
+ gatt = null;
160
+ sendConnectionEvent(device, "BleManagerDisconnectPeripheral", BluetoothGatt.GATT_SUCCESS);
161
+ }
162
+ Log.d(BleManager.LOG_TAG, "Disconnect");
163
+ } catch (Exception e) {
164
+ sendConnectionEvent(device, "BleManagerDisconnectPeripheral", BluetoothGatt.GATT_FAILURE);
165
+ Log.d(BleManager.LOG_TAG, "Error on disconnect", e);
166
+ }
167
+ } else
168
+ Log.d(BleManager.LOG_TAG, "GATT is null");
169
+ if (callback != null)
170
+ callback.invoke();
171
+ });
172
+ }
173
+
174
+ public WritableMap asWritableMap() {
175
+ WritableMap map = Arguments.createMap();
176
+ WritableMap advertising = Arguments.createMap();
177
+
178
+ try {
179
+ map.putString("name", device.getName());
180
+ map.putString("id", device.getAddress()); // mac address
181
+ map.putInt("rssi", advertisingRSSI);
182
+
183
+ String name = device.getName();
184
+ if (name != null)
185
+ advertising.putString("localName", name);
186
+
187
+ advertising.putMap("manufacturerData", byteArrayToWritableMap(advertisingDataBytes));
188
+
189
+ // No scanResult to access so we can't check if peripheral is connectable
190
+ advertising.putBoolean("isConnectable", true);
191
+
192
+ map.putMap("advertising", advertising);
193
+ } catch (Exception e) { // this shouldn't happen
194
+ e.printStackTrace();
195
+ }
196
+
197
+ return map;
198
+ }
199
+
200
+ public WritableMap asWritableMap(BluetoothGatt gatt) {
201
+
202
+ WritableMap map = asWritableMap();
203
+
204
+ WritableArray servicesArray = Arguments.createArray();
205
+ WritableArray characteristicsArray = Arguments.createArray();
206
+
207
+ if (connected && gatt != null) {
208
+ for (Iterator<BluetoothGattService> it = gatt.getServices().iterator(); it.hasNext(); ) {
209
+ BluetoothGattService service = it.next();
210
+ WritableMap serviceMap = Arguments.createMap();
211
+ serviceMap.putString("uuid", UUIDHelper.uuidToString(service.getUuid()));
212
+
213
+ for (Iterator<BluetoothGattCharacteristic> itCharacteristic = service.getCharacteristics()
214
+ .iterator(); itCharacteristic.hasNext(); ) {
215
+ BluetoothGattCharacteristic characteristic = itCharacteristic.next();
216
+ WritableMap characteristicsMap = Arguments.createMap();
217
+
218
+ characteristicsMap.putString("service", UUIDHelper.uuidToString(service.getUuid()));
219
+ characteristicsMap.putString("characteristic", UUIDHelper.uuidToString(characteristic.getUuid()));
220
+
221
+ characteristicsMap.putMap("properties", Helper.decodeProperties(characteristic));
222
+
223
+ if (characteristic.getPermissions() > 0) {
224
+ characteristicsMap.putMap("permissions", Helper.decodePermissions(characteristic));
225
+ }
226
+
227
+ WritableArray descriptorsArray = Arguments.createArray();
228
+
229
+ for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
230
+ WritableMap descriptorMap = Arguments.createMap();
231
+ descriptorMap.putString("uuid", UUIDHelper.uuidToString(descriptor.getUuid()));
232
+ if (descriptor.getValue() != null) {
233
+ descriptorMap.putString("value",
234
+ Base64.encodeToString(descriptor.getValue(), Base64.NO_WRAP));
235
+ } else {
236
+ descriptorMap.putString("value", null);
237
+ }
238
+
239
+ if (descriptor.getPermissions() > 0) {
240
+ descriptorMap.putMap("permissions", Helper.decodePermissions(descriptor));
241
+ }
242
+ descriptorsArray.pushMap(descriptorMap);
243
+ }
244
+ if (descriptorsArray.size() > 0) {
245
+ characteristicsMap.putArray("descriptors", descriptorsArray);
246
+ }
247
+ characteristicsArray.pushMap(characteristicsMap);
248
+ }
249
+ servicesArray.pushMap(serviceMap);
250
+ }
251
+ map.putArray("services", servicesArray);
252
+ map.putArray("characteristics", characteristicsArray);
253
+ }
254
+
255
+ return map;
256
+ }
257
+
258
+ static WritableMap byteArrayToWritableMap(byte[] bytes) throws JSONException {
259
+ WritableMap object = Arguments.createMap();
260
+ object.putString("CDVType", "ArrayBuffer");
261
+ object.putString("data", bytes != null ? Base64.encodeToString(bytes, Base64.NO_WRAP) : null);
262
+ object.putArray("bytes", bytes != null ? BleManager.bytesToWritableArray(bytes) : null);
263
+ return object;
264
+ }
265
+
266
+ public boolean isConnected() {
267
+ return connected;
268
+ }
269
+
270
+ public boolean isConnecting() {
271
+ return connecting;
272
+ }
273
+
274
+ public BluetoothDevice getDevice() {
275
+ return device;
276
+ }
272
277
 
273
278
  @Override
274
279
  public void onServicesDiscovered(BluetoothGatt gatt, int status) {
275
280
  super.onServicesDiscovered(gatt, status);
276
281
  mainHandler.post(() -> {
277
- if (retrieveServicesCallback != null) {
278
- WritableMap map = this.asWritableMap(gatt);
282
+ WritableMap map = this.asWritableMap(gatt);
283
+ for (Callback retrieveServicesCallback: retrieveServicesCallbacks) {
279
284
  retrieveServicesCallback.invoke(null, map);
280
- retrieveServicesCallback = null;
281
285
  }
286
+ retrieveServicesCallbacks.clear();
282
287
  completedCommand();
283
288
  });
284
289
  }
285
290
 
286
- @Override
287
- public void onConnectionStateChange(BluetoothGatt gatta, int status, final int newState) {
291
+ @Override
292
+ public void onConnectionStateChange(BluetoothGatt gatta, int status, final int newState) {
288
293
 
289
- Log.d(BleManager.LOG_TAG, "onConnectionStateChange to " + newState + " on peripheral: " + device.getAddress()
290
- + " with status " + status);
294
+ Log.d(BleManager.LOG_TAG, "onConnectionStateChange to " + newState + " on peripheral: " + device.getAddress()
295
+ + " with status " + status);
291
296
 
292
- mainHandler.post(() -> {
293
- gatt = gatta;
297
+ mainHandler.post(() -> {
298
+ gatt = gatta;
294
299
 
295
- if (status != BluetoothGatt.GATT_SUCCESS) {
296
- gatt.close();
297
- }
300
+ if (status != BluetoothGatt.GATT_SUCCESS) {
301
+ gatt.close();
302
+ }
298
303
 
299
- connecting = false;
300
- if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
301
- connected = true;
302
-
303
- discoverServicesRunnable = new Runnable() {
304
- @Override
305
- public void run() {
306
- try {
307
- gatt.discoverServices();
308
- } catch (NullPointerException e) {
309
- Log.d(BleManager.LOG_TAG, "onConnectionStateChange connected but gatt of Run method was null");
310
- }
311
- discoverServicesRunnable = null;
312
- }
313
- };
304
+ connecting = false;
305
+ if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) {
306
+ connected = true;
307
+
308
+ discoverServicesRunnable = new Runnable() {
309
+ @Override
310
+ public void run() {
311
+ try {
312
+ gatt.discoverServices();
313
+ } catch (NullPointerException e) {
314
+ Log.d(BleManager.LOG_TAG, "onConnectionStateChange connected but gatt of Run method was null");
315
+ }
316
+ discoverServicesRunnable = null;
317
+ }
318
+ };
314
319
 
315
- mainHandler.post(discoverServicesRunnable);
320
+ mainHandler.post(discoverServicesRunnable);
316
321
 
317
- sendConnectionEvent(device, "BleManagerConnectPeripheral", status);
322
+ sendConnectionEvent(device, "BleManagerConnectPeripheral", status);
318
323
 
319
- if (connectCallback != null) {
320
- Log.d(BleManager.LOG_TAG, "Connected to: " + device.getAddress());
324
+ Log.d(BleManager.LOG_TAG, "Connected to: " + device.getAddress());
325
+ for (Callback connectCallback: connectCallbacks) {
321
326
  connectCallback.invoke();
322
- connectCallback = null;
323
327
  }
328
+ connectCallbacks.clear();
324
329
 
325
- } else if (newState == BluetoothProfile.STATE_DISCONNECTED || status != BluetoothGatt.GATT_SUCCESS) {
330
+ } else if (newState == BluetoothProfile.STATE_DISCONNECTED || status != BluetoothGatt.GATT_SUCCESS) {
326
331
 
327
- if (discoverServicesRunnable != null) {
328
- mainHandler.removeCallbacks(discoverServicesRunnable);
329
- discoverServicesRunnable = null;
330
- }
332
+ if (discoverServicesRunnable != null) {
333
+ mainHandler.removeCallbacks(discoverServicesRunnable);
334
+ discoverServicesRunnable = null;
335
+ }
331
336
 
332
- List<Callback> callbacks = Arrays.asList(writeCallback, retrieveServicesCallback, readRSSICallback,
333
- readCallback, registerNotifyCallback, requestMTUCallback);
334
- for (Callback currentCallback : callbacks) {
335
- if (currentCallback != null) {
336
- try {
337
- currentCallback.invoke("Device disconnected");
338
- } catch (Exception e) {
339
- e.printStackTrace();
340
- } }
341
- }
342
- if (connectCallback != null) {
343
- connectCallback.invoke("Connection error");
344
- connectCallback = null;
337
+ for (Callback writeCallback: writeCallbacks) {
338
+ writeCallback.invoke("Device disconnected");
345
339
  }
346
- writeCallback = null;
347
- writeQueue.clear();
348
- readCallback = null;
349
- retrieveServicesCallback = null;
350
- readRSSICallback = null;
351
- registerNotifyCallback = null;
352
- requestMTUCallback = null;
353
- commandQueue.clear();
354
- commandQueueBusy = false;
355
- connectCallback = null;
356
- connected = false;
357
- clearBuffers();
358
- commandQueue.clear();
359
- commandQueueBusy = false;
360
-
361
- gatt.disconnect();
362
- gatt.close();
363
- gatt = null;
364
- sendConnectionEvent(device, "BleManagerDisconnectPeripheral", BluetoothGatt.GATT_SUCCESS);
340
+ writeCallbacks.clear();
365
341
 
366
- }
367
-
368
- });
369
-
370
- }
371
-
372
- public void updateRssi(int rssi) {
373
- advertisingRSSI = rssi;
374
- }
375
-
376
- public void updateData(byte[] data) {
377
- advertisingDataBytes = data;
378
- }
342
+ for (Callback retrieveServicesCallback: retrieveServicesCallbacks) {
343
+ retrieveServicesCallback.invoke("Device disconnected");
344
+ }
345
+ retrieveServicesCallbacks.clear();
379
346
 
380
- public int unsignedToBytes(byte b) {
381
- return b & 0xFF;
382
- }
347
+ for (Callback readRSSICallback: readRSSICallbacks) {
348
+ readRSSICallback.invoke("Device disconnected");
349
+ }
350
+ readRSSICallbacks.clear();
383
351
 
384
- //////
352
+ for (Callback registerNotifyCallback: registerNotifyCallbacks) {
353
+ registerNotifyCallback.invoke("Device disconnected");
354
+ }
355
+ registerNotifyCallbacks.clear();
385
356
 
386
- @Override
387
- public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
388
- super.onCharacteristicChanged(gatt, characteristic);
389
- try {
390
- String charString = characteristic.getUuid().toString();
391
- String service = characteristic.getService().getUuid().toString();
392
- NotifyBufferContainer buffer = this.bufferedCharacteristics
393
- .get(this.bufferedCharacteristicsKey(service, charString));
394
- byte[] dataValue = characteristic.getValue();
395
- if (buffer != null) {
396
- buffer.put(dataValue);
397
- Log.d(BleManager.LOG_TAG, "onCharacteristicChanged-buffering: " +
398
- buffer.size() + " from peripheral: " + device.getAddress());
399
-
400
- if (buffer.isBufferFull()) {
401
- Log.d(BleManager.LOG_TAG, "onCharacteristicChanged sending buffered data " + buffer.size());
402
-
403
- // send'm and reset
404
- dataValue = buffer.items.array();
405
- buffer.resetBuffer();
406
- } else {
407
- return;
357
+ for (Callback requestMTUCallback: requestMTUCallbacks) {
358
+ requestMTUCallback.invoke("Device disconnected");
408
359
  }
409
- }
410
- Log.d(BleManager.LOG_TAG, "onCharacteristicChanged: " + BleManager.bytesToHex(dataValue)
411
- + " from peripheral: " + device.getAddress());
412
- WritableMap map = Arguments.createMap();
413
- map.putString("peripheral", device.getAddress());
414
- map.putString("characteristic", charString);
415
- map.putString("service", service);
416
- map.putArray("value", BleManager.bytesToWritableArray(dataValue));
417
- sendEvent("BleManagerDidUpdateValueForCharacteristic", map);
418
-
419
- } catch (Exception e) {
420
- Log.d(BleManager.LOG_TAG, "onCharacteristicChanged ERROR: " + e.toString());
421
- }
422
- }
360
+ requestMTUCallbacks.clear();
423
361
 
424
- @Override
425
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
426
- super.onCharacteristicRead(gatt, characteristic, status);
362
+ for (Callback readCallback: readCallbacks) {
363
+ readCallback.invoke("Device disconnected");
364
+ }
365
+ readCallbacks.clear();
427
366
 
428
- mainHandler.post(() -> {
429
- if (status != BluetoothGatt.GATT_SUCCESS) {
430
- if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
431
- Log.d(BleManager.LOG_TAG, "Read needs bonding");
367
+ for (Callback connectCallback: connectCallbacks) {
368
+ connectCallback.invoke("Connection error");
369
+ }
370
+ connectCallbacks.clear();
371
+
372
+ writeQueue.clear();
373
+ commandQueue.clear();
374
+ commandQueueBusy = false;
375
+ connected = false;
376
+ clearBuffers();
377
+ commandQueue.clear();
378
+ commandQueueBusy = false;
379
+
380
+ gatt.disconnect();
381
+ gatt.close();
382
+ gatt = null;
383
+ sendConnectionEvent(device, "BleManagerDisconnectPeripheral", BluetoothGatt.GATT_SUCCESS);
384
+
385
+ }
386
+
387
+ });
388
+
389
+ }
390
+
391
+ public void updateRssi(int rssi) {
392
+ advertisingRSSI = rssi;
393
+ }
394
+
395
+ public void updateData(byte[] data) {
396
+ advertisingDataBytes = data;
397
+ }
398
+
399
+ public int unsignedToBytes(byte b) {
400
+ return b & 0xFF;
401
+ }
402
+
403
+ //////
404
+
405
+ @Override
406
+ public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
407
+ super.onCharacteristicChanged(gatt, characteristic);
408
+ try {
409
+ String charString = characteristic.getUuid().toString();
410
+ String service = characteristic.getService().getUuid().toString();
411
+ NotifyBufferContainer buffer = this.bufferedCharacteristics
412
+ .get(this.bufferedCharacteristicsKey(service, charString));
413
+ byte[] dataValue = characteristic.getValue();
414
+ if (buffer != null) {
415
+ buffer.put(dataValue);
416
+ Log.d(BleManager.LOG_TAG, "onCharacteristicChanged-buffering: " +
417
+ buffer.size() + " from peripheral: " + device.getAddress());
418
+
419
+ if (buffer.isBufferFull()) {
420
+ Log.d(BleManager.LOG_TAG, "onCharacteristicChanged sending buffered data " + buffer.size());
421
+
422
+ // send'm and reset
423
+ dataValue = buffer.items.array();
424
+ buffer.resetBuffer();
425
+ } else {
426
+ return;
427
+ }
428
+ }
429
+ Log.d(BleManager.LOG_TAG, "onCharacteristicChanged: " + BleManager.bytesToHex(dataValue)
430
+ + " from peripheral: " + device.getAddress());
431
+ WritableMap map = Arguments.createMap();
432
+ map.putString("peripheral", device.getAddress());
433
+ map.putString("characteristic", charString);
434
+ map.putString("service", service);
435
+ map.putArray("value", BleManager.bytesToWritableArray(dataValue));
436
+ sendEvent("BleManagerDidUpdateValueForCharacteristic", map);
437
+
438
+ } catch (Exception e) {
439
+ Log.d(BleManager.LOG_TAG, "onCharacteristicChanged ERROR: " + e);
440
+ }
441
+ }
442
+
443
+ @Override
444
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
445
+ super.onCharacteristicRead(gatt, characteristic, status);
446
+
447
+ mainHandler.post(() -> {
448
+ if (status != BluetoothGatt.GATT_SUCCESS) {
449
+ if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
450
+ Log.d(BleManager.LOG_TAG, "Read needs bonding");
451
+ }
452
+
453
+ for (Callback readCallback: readCallbacks) {
454
+ readCallback.invoke(
455
+ "Error reading " + characteristic.getUuid() + " status=" + status,
456
+ null
457
+ );
432
458
  }
433
- readCallback.invoke("Error reading " + characteristic.getUuid() + " status=" + status, null);
434
- readCallback = null;
435
- } else if (readCallback != null) {
459
+ readCallbacks.clear();
460
+ } else if (!readCallbacks.isEmpty()) {
436
461
  final byte[] dataValue = copyOf(characteristic.getValue());
437
- readCallback.invoke(null, BleManager.bytesToWritableArray(dataValue));
438
- readCallback = null;
462
+
463
+ for (Callback readCallback: readCallbacks) {
464
+ readCallback.invoke(null, BleManager.bytesToWritableArray(dataValue));
465
+ }
466
+ readCallbacks.clear();
439
467
  }
440
468
  completedCommand();
441
469
  });
442
470
 
443
- }
471
+ }
444
472
 
445
- @Override
446
- public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
447
- super.onCharacteristicWrite(gatt, characteristic, status);
473
+ @Override
474
+ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
475
+ super.onCharacteristicWrite(gatt, characteristic, status);
448
476
 
449
477
  mainHandler.post(() -> {
450
478
  if (writeQueue.size() > 0) {
451
479
  byte[] data = writeQueue.get(0);
452
480
  writeQueue.remove(0);
453
- doWrite(characteristic, data, writeCallback);
481
+ doWrite(characteristic, data, null);
454
482
  } else if (status != BluetoothGatt.GATT_SUCCESS) {
455
483
  if (status == GATT_AUTH_FAIL || status == GATT_INSUFFICIENT_AUTHENTICATION) {
456
484
  Log.d(BleManager.LOG_TAG, "Write needs bonding");
457
485
  // *not* doing completedCommand()
458
486
  return;
459
487
  }
460
- writeCallback.invoke( "Error writing " + characteristic.getUuid() + " status=" + status, null);
461
- writeCallback = null;
462
- } else if (writeCallback != null) {
463
- writeCallback.invoke();
464
- writeCallback = null;
488
+ for (Callback writeCallback: writeCallbacks) {
489
+ writeCallback.invoke("Error writing " + characteristic.getUuid() + " status=" + status, null);
490
+ }
491
+ writeCallbacks.clear();
492
+ } else if (!writeCallbacks.isEmpty()) {
493
+ for (Callback writeCallback: writeCallbacks) {
494
+ writeCallback.invoke();
495
+ }
496
+ writeCallbacks.clear();
465
497
  }
466
498
  completedCommand();
467
499
  });
@@ -470,268 +502,278 @@ public class Peripheral extends BluetoothGattCallback {
470
502
  @Override
471
503
  public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
472
504
  mainHandler.post(() -> {
473
- if (registerNotifyCallback != null) {
505
+ if (!registerNotifyCallbacks.isEmpty()) {
474
506
  if (status == BluetoothGatt.GATT_SUCCESS) {
475
- registerNotifyCallback.invoke();
507
+ for (Callback registerNotifyCallback: registerNotifyCallbacks) {
508
+ registerNotifyCallback.invoke();
509
+ }
476
510
  Log.d(BleManager.LOG_TAG, "onDescriptorWrite success");
477
511
  } else {
478
- registerNotifyCallback.invoke("Error writing descriptor status=" + status, null);
512
+ for (Callback registerNotifyCallback: registerNotifyCallbacks) {
513
+ registerNotifyCallback.invoke("Error writing descriptor status=" + status, null);
514
+ }
479
515
  Log.e(BleManager.LOG_TAG, "Error writing descriptor status=" + status);
480
516
  }
481
517
 
482
- registerNotifyCallback = null;
483
- } else {
484
- Log.e(BleManager.LOG_TAG, "onDescriptorWrite with no callback");
485
- }
518
+ registerNotifyCallbacks.clear();
519
+ } else {
520
+ Log.e(BleManager.LOG_TAG, "onDescriptorWrite with no callback");
521
+ }
486
522
 
487
- completedCommand();
488
- });
489
- }
523
+ completedCommand();
524
+ });
525
+ }
490
526
 
491
- @Override
492
- public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
493
- super.onReadRemoteRssi(gatt, rssi, status);
527
+ @Override
528
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
529
+ super.onReadRemoteRssi(gatt, rssi, status);
494
530
 
495
531
  mainHandler.post(() -> {
496
- if (readRSSICallback != null) {
532
+ if (!readRSSICallbacks.isEmpty()) {
497
533
  if (status == BluetoothGatt.GATT_SUCCESS) {
498
534
  updateRssi(rssi);
499
- readRSSICallback.invoke(null, rssi);
535
+ for (Callback readRSSICallback: readRSSICallbacks) {
536
+ readRSSICallback.invoke(null, rssi);
537
+ }
500
538
  } else {
501
- readRSSICallback.invoke("Error reading RSSI status=" + status, null);
539
+ for (Callback readRSSICallback: readRSSICallbacks) {
540
+ readRSSICallback.invoke("Error reading RSSI status=" + status, null);
541
+ }
502
542
  }
503
543
 
504
- readRSSICallback = null;
505
- }
506
-
507
- completedCommand();
508
- });
509
- }
510
-
511
- private String bufferedCharacteristicsKey(String serviceUUID, String characteristicUUID) {
512
- return serviceUUID + "-" + characteristicUUID;
513
- }
514
-
515
- private void clearBuffers() {
516
- for (Map.Entry<String, NotifyBufferContainer> entry : this.bufferedCharacteristics.entrySet())
517
- entry.getValue().resetBuffer();
518
- }
519
-
520
- private void setNotify(UUID serviceUUID, UUID characteristicUUID, final Boolean notify, Callback callback) {
521
- if (! isConnected() || gatt == null) {
522
- callback.invoke("Device is not connected", null);
523
- completedCommand();
524
- return;
525
- }
526
-
527
- BluetoothGattService service = gatt.getService(serviceUUID);
528
- final BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
529
-
530
- if (characteristic == null) {
531
- callback.invoke("Characteristic " + characteristicUUID + " not found");
532
- completedCommand();
533
- return;
534
- }
535
-
536
- if (! gatt.setCharacteristicNotification(characteristic, notify)) {
537
- callback.invoke("Failed to register notification for " + characteristicUUID);
538
- completedCommand();
539
- return;
540
- }
541
-
542
- final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUIDHelper.uuidFromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
543
- if (descriptor == null) {
544
- callback.invoke("Set notification failed for " + characteristicUUID);
545
- completedCommand();
546
- return;
547
- }
548
-
549
- // Prefer notify over indicate
550
- byte[] value;
551
- if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
552
- Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
553
- value = notify ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
554
- } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
555
- Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
556
- value = notify ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
557
- } else {
558
- String msg = "Characteristic " + characteristicUUID + " does not have NOTIFY or INDICATE property set";
559
- Log.d(BleManager.LOG_TAG, msg);
560
- callback.invoke(msg);
561
- completedCommand();
562
- return;
563
- }
564
- final byte[] finalValue = notify ? value : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
565
- final Callback finalCallback = callback;
566
-
567
- boolean result = false;
568
- try {
569
- result = gatt.setCharacteristicNotification(characteristic, notify);
570
- // Then write to descriptor
571
- descriptor.setValue(finalValue);
572
- registerNotifyCallback = finalCallback;
573
- result &= gatt.writeDescriptor(descriptor);
574
- } catch(Exception e) {
575
- Log.d(BleManager.LOG_TAG, "Exception in setNotify", e);
576
- }
544
+ readRSSICallbacks.clear();
545
+ }
546
+
547
+ completedCommand();
548
+ });
549
+ }
550
+
551
+ private String bufferedCharacteristicsKey(String serviceUUID, String characteristicUUID) {
552
+ return serviceUUID + "-" + characteristicUUID;
553
+ }
554
+
555
+ private void clearBuffers() {
556
+ for (Map.Entry<String, NotifyBufferContainer> entry : this.bufferedCharacteristics.entrySet())
557
+ entry.getValue().resetBuffer();
558
+ }
559
+
560
+ private void setNotify(UUID serviceUUID, UUID characteristicUUID, final Boolean notify, Callback callback) {
561
+ if (!isConnected() || gatt == null) {
562
+ callback.invoke("Device is not connected", null);
563
+ completedCommand();
564
+ return;
565
+ }
566
+
567
+ BluetoothGattService service = gatt.getService(serviceUUID);
568
+ final BluetoothGattCharacteristic characteristic = findNotifyCharacteristic(service, characteristicUUID);
569
+
570
+ if (characteristic == null) {
571
+ callback.invoke("Characteristic " + characteristicUUID + " not found");
572
+ completedCommand();
573
+ return;
574
+ }
575
+
576
+ if (!gatt.setCharacteristicNotification(characteristic, notify)) {
577
+ callback.invoke("Failed to register notification for " + characteristicUUID);
578
+ completedCommand();
579
+ return;
580
+ }
581
+
582
+ final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUIDHelper.uuidFromString(CHARACTERISTIC_NOTIFICATION_CONFIG));
583
+ if (descriptor == null) {
584
+ callback.invoke("Set notification failed for " + characteristicUUID);
585
+ completedCommand();
586
+ return;
587
+ }
588
+
589
+ // Prefer notify over indicate
590
+ byte[] value;
591
+ if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) {
592
+ Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set NOTIFY");
593
+ value = notify ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
594
+ } else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) {
595
+ Log.d(BleManager.LOG_TAG, "Characteristic " + characteristicUUID + " set INDICATE");
596
+ value = notify ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
597
+ } else {
598
+ String msg = "Characteristic " + characteristicUUID + " does not have NOTIFY or INDICATE property set";
599
+ Log.d(BleManager.LOG_TAG, msg);
600
+ callback.invoke(msg);
601
+ completedCommand();
602
+ return;
603
+ }
604
+ final byte[] finalValue = notify ? value : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
605
+
606
+ boolean result = false;
607
+ try {
608
+ result = gatt.setCharacteristicNotification(characteristic, notify);
609
+ // Then write to descriptor
610
+ descriptor.setValue(finalValue);
611
+ registerNotifyCallbacks.addLast(callback);
612
+ result &= gatt.writeDescriptor(descriptor);
613
+ } catch (Exception e) {
614
+ Log.d(BleManager.LOG_TAG, "Exception in setNotify", e);
615
+ }
577
616
 
578
617
  if (! result) {
579
- callback.invoke( "writeDescriptor failed for descriptor: " + descriptor.getUuid(), null);
580
- registerNotifyCallback = null;
581
- completedCommand();
582
- }
583
- }
584
-
585
- public void registerNotify(UUID serviceUUID, UUID characteristicUUID, Integer buffer, Callback callback) {
586
- if (!enqueue(() -> {
587
- Log.d(BleManager.LOG_TAG, "registerNotify");
588
- if (buffer > 1) {
589
- Log.d(BleManager.LOG_TAG, "registerNotify using buffer");
590
- String bufferKey = this.bufferedCharacteristicsKey(serviceUUID.toString(), characteristicUUID.toString());
591
- this.bufferedCharacteristics.put(bufferKey, new NotifyBufferContainer(buffer));
592
- }
593
- this.setNotify(serviceUUID, characteristicUUID, true, callback);
594
- })) {
595
- Log.e(BleManager.LOG_TAG, "Could not enqueue setNotify command to register notify");
596
- }
597
- }
598
-
599
- public void removeNotify(UUID serviceUUID, UUID characteristicUUID, Callback callback) {
600
- if (!enqueue(() -> {
601
- Log.d(BleManager.LOG_TAG, "removeNotify");
602
- String bufferKey = this.bufferedCharacteristicsKey(serviceUUID.toString(), characteristicUUID.toString());
603
- if (this.bufferedCharacteristics.containsKey(bufferKey)) {
604
- NotifyBufferContainer buffer = this.bufferedCharacteristics.get(bufferKey);
605
- this.bufferedCharacteristics.remove(bufferKey);
618
+ for (Callback registerNotifyCallback: registerNotifyCallbacks) {
619
+ registerNotifyCallback.invoke("writeDescriptor failed for descriptor: " + descriptor.getUuid(), null);
606
620
  }
607
- this.setNotify(serviceUUID, characteristicUUID, false, callback);
608
- })) {
609
- Log.e(BleManager.LOG_TAG, "Could not enqueue setNotify command to remove notify");
610
- }
611
- }
612
-
613
- // Some devices reuse UUIDs across characteristics, so we can't use
614
- // service.getCharacteristic(characteristicUUID)
615
- // instead check the UUID and properties for each characteristic in the service
616
- // until we find the best match
617
- // This function prefers Notify over Indicate
618
- private BluetoothGattCharacteristic findNotifyCharacteristic(BluetoothGattService service,
619
- UUID characteristicUUID) {
620
-
621
- try {
622
- // Check for Notify first
623
- List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
624
- for (BluetoothGattCharacteristic characteristic : characteristics) {
625
- if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0
626
- && characteristicUUID.equals(characteristic.getUuid())) {
627
- return characteristic;
628
- }
629
- }
630
-
631
- // If there wasn't Notify Characteristic, check for Indicate
632
- for (BluetoothGattCharacteristic characteristic : characteristics) {
633
- if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0
634
- && characteristicUUID.equals(characteristic.getUuid())) {
635
- return characteristic;
636
- }
637
- }
638
-
639
- // As a last resort, try and find ANY characteristic with this UUID, even if it
640
- // doesn't have the correct properties
641
- return service.getCharacteristic(characteristicUUID);
642
- } catch (Exception e) {
643
- Log.e(BleManager.LOG_TAG, "Error retriving characteristic " + characteristicUUID, e);
644
- return null;
621
+ registerNotifyCallbacks.clear();
622
+ completedCommand();
645
623
  }
646
624
  }
647
625
 
648
- public void read(UUID serviceUUID, UUID characteristicUUID, final Callback callback) {
649
- enqueue(() -> {
650
- if (!isConnected() || gatt == null) {
651
- callback.invoke("Device is not connected", null);
652
- completedCommand();
653
- return;
654
- }
655
-
656
- BluetoothGattService service = gatt.getService(serviceUUID);
657
- final BluetoothGattCharacteristic characteristic = findReadableCharacteristic(service, characteristicUUID);
658
-
659
- if (characteristic == null) {
660
- callback.invoke("Characteristic " + characteristicUUID + " not found.", null);
661
- completedCommand();
662
- return;
663
- }
664
-
665
- readCallback = callback;
626
+ public void registerNotify(UUID serviceUUID, UUID characteristicUUID, Integer buffer, Callback callback) {
627
+ if (!enqueue(() -> {
628
+ Log.d(BleManager.LOG_TAG, "registerNotify");
629
+ if (buffer > 1) {
630
+ Log.d(BleManager.LOG_TAG, "registerNotify using buffer");
631
+ String bufferKey = this.bufferedCharacteristicsKey(serviceUUID.toString(), characteristicUUID.toString());
632
+ this.bufferedCharacteristics.put(bufferKey, new NotifyBufferContainer(buffer));
633
+ }
634
+ this.setNotify(serviceUUID, characteristicUUID, true, callback);
635
+ })) {
636
+ Log.e(BleManager.LOG_TAG, "Could not enqueue setNotify command to register notify");
637
+ }
638
+ }
639
+
640
+ public void removeNotify(UUID serviceUUID, UUID characteristicUUID, Callback callback) {
641
+ if (!enqueue(() -> {
642
+ Log.d(BleManager.LOG_TAG, "removeNotify");
643
+ String bufferKey = this.bufferedCharacteristicsKey(serviceUUID.toString(), characteristicUUID.toString());
644
+ if (this.bufferedCharacteristics.containsKey(bufferKey)) {
645
+ NotifyBufferContainer buffer = this.bufferedCharacteristics.get(bufferKey);
646
+ this.bufferedCharacteristics.remove(bufferKey);
647
+ }
648
+ this.setNotify(serviceUUID, characteristicUUID, false, callback);
649
+ })) {
650
+ Log.e(BleManager.LOG_TAG, "Could not enqueue setNotify command to remove notify");
651
+ }
652
+ }
653
+
654
+ // Some devices reuse UUIDs across characteristics, so we can't use
655
+ // service.getCharacteristic(characteristicUUID)
656
+ // instead check the UUID and properties for each characteristic in the service
657
+ // until we find the best match
658
+ // This function prefers Notify over Indicate
659
+ private BluetoothGattCharacteristic findNotifyCharacteristic(BluetoothGattService service,
660
+ UUID characteristicUUID) {
661
+
662
+ try {
663
+ // Check for Notify first
664
+ List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
665
+ for (BluetoothGattCharacteristic characteristic : characteristics) {
666
+ if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0
667
+ && characteristicUUID.equals(characteristic.getUuid())) {
668
+ return characteristic;
669
+ }
670
+ }
671
+
672
+ // If there wasn't Notify Characteristic, check for Indicate
673
+ for (BluetoothGattCharacteristic characteristic : characteristics) {
674
+ if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0
675
+ && characteristicUUID.equals(characteristic.getUuid())) {
676
+ return characteristic;
677
+ }
678
+ }
679
+
680
+ // As a last resort, try and find ANY characteristic with this UUID, even if it
681
+ // doesn't have the correct properties
682
+ return service.getCharacteristic(characteristicUUID);
683
+ } catch (Exception e) {
684
+ Log.e(BleManager.LOG_TAG, "Error retriving characteristic " + characteristicUUID, e);
685
+ return null;
686
+ }
687
+ }
688
+
689
+ public void read(UUID serviceUUID, UUID characteristicUUID, final Callback callback) {
690
+ enqueue(() -> {
691
+ if (!isConnected() || gatt == null) {
692
+ callback.invoke("Device is not connected", null);
693
+ completedCommand();
694
+ return;
695
+ }
696
+
697
+ BluetoothGattService service = gatt.getService(serviceUUID);
698
+ final BluetoothGattCharacteristic characteristic = findReadableCharacteristic(service, characteristicUUID);
699
+
700
+ if (characteristic == null) {
701
+ callback.invoke("Characteristic " + characteristicUUID + " not found.", null);
702
+ completedCommand();
703
+ return;
704
+ }
705
+
706
+ this.readCallbacks.addLast(callback);
666
707
  if (!gatt.readCharacteristic(characteristic)) {
667
- callback.invoke("Read failed", null);
668
- readCallback = null;
708
+ for (Callback readCallback: readCallbacks) {
709
+ readCallback.invoke("Read failed", null);
710
+ }
711
+ readCallbacks.clear();
669
712
  completedCommand();
670
713
  }
671
-
672
714
  });
673
715
  }
674
716
 
675
- private byte[] copyOf(byte[] source) {
676
- if (source == null) return new byte[0];
677
- final int sourceLength = source.length;
678
- final byte[] copy = new byte[sourceLength];
679
- System.arraycopy(source, 0, copy, 0, sourceLength);
680
- return copy;
681
- }
682
-
683
- private boolean enqueue(Runnable command) {
684
- final boolean result = commandQueue.add(command);
685
- if (result) {
686
- nextCommand();
687
- } else {
688
- Log.d(BleManager.LOG_TAG, "could not enqueue command");
689
- }
690
- return result;
691
- }
692
-
693
- private void completedCommand() {
694
- commandQueue.poll();
695
- commandQueueBusy = false;
696
- nextCommand();
697
- }
698
-
699
- private void nextCommand() {
700
- synchronized (this) {
701
- if (commandQueueBusy) {
702
- Log.d(BleManager.LOG_TAG, "Command queue busy");
703
- return;
704
- }
705
-
706
- final Runnable nextCommand = commandQueue.peek();
707
- if (nextCommand == null) {
708
- Log.d(BleManager.LOG_TAG, "Command queue empty");
709
- return;
710
- }
711
-
712
- // Check if we still have a valid gatt object
713
- if (gatt == null) {
714
- Log.d(BleManager.LOG_TAG, "Error, gatt is null");
715
- commandQueue.clear();
716
- commandQueueBusy = false;
717
- return;
718
- }
719
-
720
- // Execute the next command in the queue
721
- commandQueueBusy = true;
722
- mainHandler.post(new Runnable() {
723
- @Override
724
- public void run() {
725
- try {
726
- nextCommand.run();
727
- } catch (Exception ex) {
728
- Log.d(BleManager.LOG_TAG, "Error, command exception");
729
- completedCommand();
730
- }
731
- }
732
- });
733
- }
734
- }
717
+ private byte[] copyOf(byte[] source) {
718
+ if (source == null) return new byte[0];
719
+ final int sourceLength = source.length;
720
+ final byte[] copy = new byte[sourceLength];
721
+ System.arraycopy(source, 0, copy, 0, sourceLength);
722
+ return copy;
723
+ }
724
+
725
+ private boolean enqueue(Runnable command) {
726
+ final boolean result = commandQueue.add(command);
727
+ if (result) {
728
+ nextCommand();
729
+ } else {
730
+ Log.d(BleManager.LOG_TAG, "could not enqueue command");
731
+ }
732
+ return result;
733
+ }
734
+
735
+ private void completedCommand() {
736
+ commandQueue.poll();
737
+ commandQueueBusy = false;
738
+ nextCommand();
739
+ }
740
+
741
+ private void nextCommand() {
742
+ synchronized (this) {
743
+ if (commandQueueBusy) {
744
+ Log.d(BleManager.LOG_TAG, "Command queue busy");
745
+ return;
746
+ }
747
+
748
+ final Runnable nextCommand = commandQueue.peek();
749
+ if (nextCommand == null) {
750
+ Log.d(BleManager.LOG_TAG, "Command queue empty");
751
+ return;
752
+ }
753
+
754
+ // Check if we still have a valid gatt object
755
+ if (gatt == null) {
756
+ Log.d(BleManager.LOG_TAG, "Error, gatt is null");
757
+ commandQueue.clear();
758
+ commandQueueBusy = false;
759
+ return;
760
+ }
761
+
762
+ // Execute the next command in the queue
763
+ commandQueueBusy = true;
764
+ mainHandler.post(new Runnable() {
765
+ @Override
766
+ public void run() {
767
+ try {
768
+ nextCommand.run();
769
+ } catch (Exception ex) {
770
+ Log.d(BleManager.LOG_TAG, "Error, command exception");
771
+ completedCommand();
772
+ }
773
+ }
774
+ });
775
+ }
776
+ }
735
777
 
736
778
  public void readRSSI(final Callback callback) {
737
779
  if (!enqueue(() -> {
@@ -744,10 +786,12 @@ public class Peripheral extends BluetoothGattCallback {
744
786
  completedCommand();
745
787
  return;
746
788
  } else {
747
- readRSSICallback = callback;
789
+ readRSSICallbacks.addLast(callback);
748
790
  if (!gatt.readRemoteRssi()) {
749
- callback.invoke("Read RSSI failed", null);
750
- readRSSICallback = null;
791
+ for (Callback readRSSICallback: readRSSICallbacks) {
792
+ readRSSICallback.invoke("Read RSSI failed", null);
793
+ }
794
+ readRSSICallbacks.clear();
751
795
  completedCommand();
752
796
  }
753
797
  }
@@ -756,67 +800,67 @@ public class Peripheral extends BluetoothGattCallback {
756
800
  }
757
801
  }
758
802
 
759
- public void refreshCache(Callback callback) {
760
- enqueue(() -> {
761
- try {
762
- Method localMethod = gatt.getClass().getMethod("refresh", new Class[0]);
763
- if (localMethod != null) {
764
- boolean res = ((Boolean) localMethod.invoke(gatt, new Object[0])).booleanValue();
765
- callback.invoke(null, res);
766
- } else {
767
- callback.invoke("Could not refresh cache for device.");
768
- }
769
- } catch (Exception localException) {
770
- Log.e(TAG, "An exception occured while refreshing device");
771
- callback.invoke(localException.getMessage());
772
- } finally {
773
- completedCommand();
774
- }
775
- });
776
- }
777
-
778
- public void retrieveServices(Callback callback) {
779
- enqueue(() -> {
780
- if (!isConnected()) {
781
- callback.invoke("Device is not connected", null);
782
- completedCommand();
783
- return;
784
- } else if (gatt == null) {
785
- callback.invoke("BluetoothGatt is null", null);
786
- completedCommand();
787
- return;
788
- } else {
789
- this.retrieveServicesCallback = callback;
790
- gatt.discoverServices();
791
- }
792
- });
793
- }
794
-
795
- // Some peripherals re-use UUIDs for multiple characteristics so we need to
796
- // check the properties
797
- // and UUID of all characteristics instead of using
798
- // service.getCharacteristic(characteristicUUID)
799
- private BluetoothGattCharacteristic findReadableCharacteristic(BluetoothGattService service,
800
- UUID characteristicUUID) {
801
-
802
- if (service != null) {
803
- int read = BluetoothGattCharacteristic.PROPERTY_READ;
804
-
805
- List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
806
- for (BluetoothGattCharacteristic characteristic : characteristics) {
807
- if ((characteristic.getProperties() & read) != 0
808
- && characteristicUUID.equals(characteristic.getUuid())) {
809
- return characteristic;
810
- }
811
- }
812
-
813
- // As a last resort, try and find ANY characteristic with this UUID, even if it
814
- // doesn't have the correct properties
815
- return service.getCharacteristic(characteristicUUID);
816
- }
817
-
818
- return null;
819
- }
803
+ public void refreshCache(Callback callback) {
804
+ enqueue(() -> {
805
+ try {
806
+ Method localMethod = gatt.getClass().getMethod("refresh", new Class[0]);
807
+ if (localMethod != null) {
808
+ boolean res = ((Boolean) localMethod.invoke(gatt, new Object[0])).booleanValue();
809
+ callback.invoke(null, res);
810
+ } else {
811
+ callback.invoke("Could not refresh cache for device.");
812
+ }
813
+ } catch (Exception localException) {
814
+ Log.e(TAG, "An exception occured while refreshing device");
815
+ callback.invoke(localException.getMessage());
816
+ } finally {
817
+ completedCommand();
818
+ }
819
+ });
820
+ }
821
+
822
+ public void retrieveServices(Callback callback) {
823
+ enqueue(() -> {
824
+ if (!isConnected()) {
825
+ callback.invoke("Device is not connected", null);
826
+ completedCommand();
827
+ return;
828
+ } else if (gatt == null) {
829
+ callback.invoke("BluetoothGatt is null", null);
830
+ completedCommand();
831
+ return;
832
+ } else {
833
+ this.retrieveServicesCallbacks.addLast(callback);
834
+ gatt.discoverServices();
835
+ }
836
+ });
837
+ }
838
+
839
+ // Some peripherals re-use UUIDs for multiple characteristics so we need to
840
+ // check the properties
841
+ // and UUID of all characteristics instead of using
842
+ // service.getCharacteristic(characteristicUUID)
843
+ private BluetoothGattCharacteristic findReadableCharacteristic(BluetoothGattService service,
844
+ UUID characteristicUUID) {
845
+
846
+ if (service != null) {
847
+ int read = BluetoothGattCharacteristic.PROPERTY_READ;
848
+
849
+ List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
850
+ for (BluetoothGattCharacteristic characteristic : characteristics) {
851
+ if ((characteristic.getProperties() & read) != 0
852
+ && characteristicUUID.equals(characteristic.getUuid())) {
853
+ return characteristic;
854
+ }
855
+ }
856
+
857
+ // As a last resort, try and find ANY characteristic with this UUID, even if it
858
+ // doesn't have the correct properties
859
+ return service.getCharacteristic(characteristicUUID);
860
+ }
861
+
862
+ return null;
863
+ }
820
864
 
821
865
  public boolean doWrite(final BluetoothGattCharacteristic characteristic, byte[] data, final Callback callback) {
822
866
  final byte[] copyOfData = copyOf(data);
@@ -824,143 +868,147 @@ public class Peripheral extends BluetoothGattCallback {
824
868
  @Override
825
869
  public void run() {
826
870
  characteristic.setValue(copyOfData);
827
- if (characteristic.getWriteType() == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
828
- writeCallback = callback;
829
- else
830
- writeCallback = null;
871
+ if (
872
+ characteristic.getWriteType() == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
873
+ && callback != null
874
+ ) {
875
+ writeCallbacks.addLast(callback);
876
+ }
831
877
  if (!gatt.writeCharacteristic(characteristic)) {
832
878
  // write without response, caller will handle the callback
833
- if (writeCallback != null) {
879
+ for (Callback writeCallback: writeCallbacks) {
834
880
  writeCallback.invoke("Write failed", writeCallback);
835
- writeCallback = null;
836
881
  }
882
+ writeCallbacks.clear();
837
883
  completedCommand();
838
884
  }
839
885
  }
840
886
  });
841
887
  }
842
888
 
843
- public void write(UUID serviceUUID, UUID characteristicUUID, byte[] data, Integer maxByteSize, Integer queueSleepTime, Callback callback, int writeType) {
844
- enqueue(() -> {
845
- if (!isConnected() || gatt == null) {
846
- callback.invoke("Device is not connected", null);
847
- completedCommand();
848
- return;
849
- }
850
-
851
- BluetoothGattService service = gatt.getService(serviceUUID);
852
- BluetoothGattCharacteristic characteristic = findWritableCharacteristic(service, characteristicUUID, writeType);
853
-
854
- if (characteristic == null) {
855
- callback.invoke("Characteristic " + characteristicUUID + " not found.");
856
- completedCommand();
857
- return;
858
- }
859
-
860
- characteristic.setWriteType(writeType);
861
-
862
- if (data.length <= maxByteSize) {
863
- if (! doWrite(characteristic, data, callback)) {
864
- callback.invoke("Write failed");
865
- } else {
866
- if (BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE == writeType) {
867
- callback.invoke();
868
- }
869
- }
870
- } else {
871
- int dataLength = data.length;
872
- int count = 0;
873
- byte[] firstMessage = null;
874
- List<byte[]> splittedMessage = new ArrayList<>();
875
-
876
- while (count < dataLength && (dataLength - count > maxByteSize)) {
877
- if (count == 0) {
878
- firstMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
879
- } else {
880
- byte[] splitMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
881
- splittedMessage.add(splitMessage);
882
- }
883
- count += maxByteSize;
884
- }
885
- if (count < dataLength) {
886
- // Other bytes in queue
887
- byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
888
- splittedMessage.add(splitMessage);
889
- }
890
-
891
- if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
892
- writeQueue.addAll(splittedMessage);
893
- if (! doWrite(characteristic, firstMessage, callback)) {
894
- writeQueue.clear();
895
- callback.invoke("Write failed");
896
- }
897
- } else {
898
- try {
899
- boolean writeError = false;
900
- if (! doWrite(characteristic, firstMessage, callback)) {
901
- writeError = true;
902
- callback.invoke("Write failed");
903
- }
904
- if (! writeError) {
905
- Thread.sleep(queueSleepTime);
906
- for (byte[] message : splittedMessage) {
907
- if (! doWrite(characteristic, message, callback)) {
908
- writeError = true;
909
- callback.invoke("Write failed");
910
- break;
911
- }
912
- Thread.sleep(queueSleepTime);
913
- }
914
- if (! writeError) {
915
- callback.invoke();
916
- }
917
- }
918
- } catch (InterruptedException e) {
919
- callback.invoke("Error during writing");
920
- }
921
- }
922
- }
923
-
924
- completedCommand();
925
- });
926
- }
927
-
928
- public void requestConnectionPriority(int connectionPriority, Callback callback) {
929
- enqueue(() -> {
930
- if (gatt != null) {
931
- if (Build.VERSION.SDK_INT >= LOLLIPOP) {
932
- boolean status = gatt.requestConnectionPriority(connectionPriority);
933
- callback.invoke(null, status);
934
- } else {
935
- callback.invoke("Requesting connection priority requires at least API level 21", null);
936
- }
937
- } else {
938
- callback.invoke("BluetoothGatt is null", null);
939
- }
940
-
941
- completedCommand();
942
- });
943
- }
944
-
945
- public void requestMTU(int mtu, Callback callback) {
946
- enqueue(() -> {
947
- if (!isConnected()) {
948
- callback.invoke("Device is not connected", null);
949
- completedCommand();
950
- return;
951
- }
952
-
953
- if (gatt == null) {
954
- callback.invoke("BluetoothGatt is null", null);
955
- completedCommand();
956
- return;
957
- }
889
+ public void write(UUID serviceUUID, UUID characteristicUUID, byte[] data, Integer maxByteSize, Integer queueSleepTime, Callback callback, int writeType) {
890
+ enqueue(() -> {
891
+ if (!isConnected() || gatt == null) {
892
+ callback.invoke("Device is not connected", null);
893
+ completedCommand();
894
+ return;
895
+ }
896
+
897
+ BluetoothGattService service = gatt.getService(serviceUUID);
898
+ BluetoothGattCharacteristic characteristic = findWritableCharacteristic(service, characteristicUUID, writeType);
899
+
900
+ if (characteristic == null) {
901
+ callback.invoke("Characteristic " + characteristicUUID + " not found.");
902
+ completedCommand();
903
+ return;
904
+ }
905
+
906
+ characteristic.setWriteType(writeType);
907
+
908
+ if (data.length <= maxByteSize) {
909
+ if (!doWrite(characteristic, data, callback)) {
910
+ callback.invoke("Write failed");
911
+ } else {
912
+ if (BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE == writeType) {
913
+ callback.invoke();
914
+ }
915
+ }
916
+ } else {
917
+ int dataLength = data.length;
918
+ int count = 0;
919
+ byte[] firstMessage = null;
920
+ List<byte[]> splittedMessage = new ArrayList<>();
921
+
922
+ while (count < dataLength && (dataLength - count > maxByteSize)) {
923
+ if (count == 0) {
924
+ firstMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
925
+ } else {
926
+ byte[] splitMessage = Arrays.copyOfRange(data, count, count + maxByteSize);
927
+ splittedMessage.add(splitMessage);
928
+ }
929
+ count += maxByteSize;
930
+ }
931
+ if (count < dataLength) {
932
+ // Other bytes in queue
933
+ byte[] splitMessage = Arrays.copyOfRange(data, count, data.length);
934
+ splittedMessage.add(splitMessage);
935
+ }
936
+
937
+ if (BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT == writeType) {
938
+ writeQueue.addAll(splittedMessage);
939
+ if (!doWrite(characteristic, firstMessage, callback)) {
940
+ writeQueue.clear();
941
+ callback.invoke("Write failed");
942
+ }
943
+ } else {
944
+ try {
945
+ boolean writeError = false;
946
+ if (!doWrite(characteristic, firstMessage, callback)) {
947
+ writeError = true;
948
+ callback.invoke("Write failed");
949
+ }
950
+ if (!writeError) {
951
+ Thread.sleep(queueSleepTime);
952
+ for (byte[] message : splittedMessage) {
953
+ if (!doWrite(characteristic, message, callback)) {
954
+ writeError = true;
955
+ callback.invoke("Write failed");
956
+ break;
957
+ }
958
+ Thread.sleep(queueSleepTime);
959
+ }
960
+ if (!writeError) {
961
+ callback.invoke();
962
+ }
963
+ }
964
+ } catch (InterruptedException e) {
965
+ callback.invoke("Error during writing");
966
+ }
967
+ }
968
+ }
969
+
970
+ completedCommand();
971
+ });
972
+ }
973
+
974
+ public void requestConnectionPriority(int connectionPriority, Callback callback) {
975
+ enqueue(() -> {
976
+ if (gatt != null) {
977
+ if (Build.VERSION.SDK_INT >= LOLLIPOP) {
978
+ boolean status = gatt.requestConnectionPriority(connectionPriority);
979
+ callback.invoke(null, status);
980
+ } else {
981
+ callback.invoke("Requesting connection priority requires at least API level 21", null);
982
+ }
983
+ } else {
984
+ callback.invoke("BluetoothGatt is null", null);
985
+ }
986
+
987
+ completedCommand();
988
+ });
989
+ }
990
+
991
+ public void requestMTU(int mtu, Callback callback) {
992
+ enqueue(() -> {
993
+ if (!isConnected()) {
994
+ callback.invoke("Device is not connected", null);
995
+ completedCommand();
996
+ return;
997
+ }
998
+
999
+ if (gatt == null) {
1000
+ callback.invoke("BluetoothGatt is null", null);
1001
+ completedCommand();
1002
+ return;
1003
+ }
958
1004
 
959
1005
  if (Build.VERSION.SDK_INT >= LOLLIPOP) {
960
- requestMTUCallback = callback;
1006
+ requestMTUCallbacks.addLast(callback);
961
1007
  if (!gatt.requestMtu(mtu)) {
962
- requestMTUCallback.invoke("Request MTU failed", null);
963
- requestMTUCallback = null;
1008
+ for (Callback requestMTUCallback: requestMTUCallbacks) {
1009
+ requestMTUCallback.invoke("Request MTU failed", null);
1010
+ }
1011
+ requestMTUCallbacks.clear();
964
1012
  completedCommand();
965
1013
  }
966
1014
  } else {
@@ -974,56 +1022,60 @@ public class Peripheral extends BluetoothGattCallback {
974
1022
  public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
975
1023
  super.onMtuChanged(gatt, mtu, status);
976
1024
  mainHandler.post(() -> {
977
- if (requestMTUCallback != null) {
1025
+ if (!requestMTUCallbacks.isEmpty()) {
978
1026
  if (status == BluetoothGatt.GATT_SUCCESS) {
979
- requestMTUCallback.invoke(null, mtu);
1027
+ for (Callback requestMTUCallback: requestMTUCallbacks) {
1028
+ requestMTUCallback.invoke(null, mtu);
1029
+ }
980
1030
  } else {
981
- requestMTUCallback.invoke("Error requesting MTU status = " + status, null);
982
- }
983
-
984
- requestMTUCallback = null;
985
- }
986
-
987
- completedCommand();
988
- });
989
- }
990
-
991
- // Some peripherals re-use UUIDs for multiple characteristics so we need to
992
- // check the properties
993
- // and UUID of all characteristics instead of using
994
- // service.getCharacteristic(characteristicUUID)
995
- private BluetoothGattCharacteristic findWritableCharacteristic(BluetoothGattService service,
996
- UUID characteristicUUID, int writeType) {
997
- try {
998
- // get write property
999
- int writeProperty = BluetoothGattCharacteristic.PROPERTY_WRITE;
1000
- if (writeType == BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) {
1001
- writeProperty = BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
1002
- }
1003
-
1004
- List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
1005
- for (BluetoothGattCharacteristic characteristic : characteristics) {
1006
- if ((characteristic.getProperties() & writeProperty) != 0
1007
- && characteristicUUID.equals(characteristic.getUuid())) {
1008
- return characteristic;
1031
+ for (Callback requestMTUCallback: requestMTUCallbacks) {
1032
+ requestMTUCallback.invoke("Error requesting MTU status = " + status, null);
1033
+ }
1009
1034
  }
1010
- }
1011
1035
 
1012
- // As a last resort, try and find ANY characteristic with this UUID, even if it
1013
- // doesn't have the correct properties
1014
- return service.getCharacteristic(characteristicUUID);
1015
- } catch (Exception e) {
1016
- Log.e(BleManager.LOG_TAG, "Error on findWritableCharacteristic", e);
1017
- return null;
1018
- }
1019
- }
1020
-
1021
- private String generateHashKey(BluetoothGattCharacteristic characteristic) {
1022
- return generateHashKey(characteristic.getService().getUuid(), characteristic);
1023
- }
1024
-
1025
- private String generateHashKey(UUID serviceUUID, BluetoothGattCharacteristic characteristic) {
1026
- return String.valueOf(serviceUUID) + "|" + characteristic.getUuid() + "|" + characteristic.getInstanceId();
1027
- }
1036
+ requestMTUCallbacks.clear();
1037
+ }
1038
+
1039
+ completedCommand();
1040
+ });
1041
+ }
1042
+
1043
+ // Some peripherals re-use UUIDs for multiple characteristics so we need to
1044
+ // check the properties
1045
+ // and UUID of all characteristics instead of using
1046
+ // service.getCharacteristic(characteristicUUID)
1047
+ private BluetoothGattCharacteristic findWritableCharacteristic(BluetoothGattService service,
1048
+ UUID characteristicUUID, int writeType) {
1049
+ try {
1050
+ // get write property
1051
+ int writeProperty = BluetoothGattCharacteristic.PROPERTY_WRITE;
1052
+ if (writeType == BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) {
1053
+ writeProperty = BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
1054
+ }
1055
+
1056
+ List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
1057
+ for (BluetoothGattCharacteristic characteristic : characteristics) {
1058
+ if ((characteristic.getProperties() & writeProperty) != 0
1059
+ && characteristicUUID.equals(characteristic.getUuid())) {
1060
+ return characteristic;
1061
+ }
1062
+ }
1063
+
1064
+ // As a last resort, try and find ANY characteristic with this UUID, even if it
1065
+ // doesn't have the correct properties
1066
+ return service.getCharacteristic(characteristicUUID);
1067
+ } catch (Exception e) {
1068
+ Log.e(BleManager.LOG_TAG, "Error on findWritableCharacteristic", e);
1069
+ return null;
1070
+ }
1071
+ }
1072
+
1073
+ private String generateHashKey(BluetoothGattCharacteristic characteristic) {
1074
+ return generateHashKey(characteristic.getService().getUuid(), characteristic);
1075
+ }
1076
+
1077
+ private String generateHashKey(UUID serviceUUID, BluetoothGattCharacteristic characteristic) {
1078
+ return serviceUUID + "|" + characteristic.getUuid() + "|" + characteristic.getInstanceId();
1079
+ }
1028
1080
 
1029
1081
  }