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