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.
- package/README.md +96 -47
- 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 +33 -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 +889 -837
- package/android/src/main/java/it/innove/ScanManager.java +24 -23
- package/dist/cjs/index.d.ts +147 -0
- package/dist/cjs/index.js +487 -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 +147 -0
- package/dist/esm/index.js +471 -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.m +87 -110
- package/ios/BleManager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/package.json +31 -4
- package/src/index.ts +569 -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 -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.
|
|
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> 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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
278
|
-
|
|
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
|
-
|
|
287
|
-
|
|
291
|
+
@Override
|
|
292
|
+
public void onConnectionStateChange(BluetoothGatt gatta, int status, final int newState) {
|
|
288
293
|
|
|
289
|
-
|
|
290
|
-
|
|
294
|
+
Log.d(BleManager.LOG_TAG, "onConnectionStateChange to " + newState + " on peripheral: " + device.getAddress()
|
|
295
|
+
+ " with status " + status);
|
|
291
296
|
|
|
292
|
-
|
|
293
|
-
|
|
297
|
+
mainHandler.post(() -> {
|
|
298
|
+
gatt = gatta;
|
|
294
299
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
300
|
+
if (status != BluetoothGatt.GATT_SUCCESS) {
|
|
301
|
+
gatt.close();
|
|
302
|
+
}
|
|
298
303
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
320
|
+
mainHandler.post(discoverServicesRunnable);
|
|
316
321
|
|
|
317
|
-
|
|
322
|
+
sendConnectionEvent(device, "BleManagerConnectPeripheral", status);
|
|
318
323
|
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
330
|
+
} else if (newState == BluetoothProfile.STATE_DISCONNECTED || status != BluetoothGatt.GATT_SUCCESS) {
|
|
326
331
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
332
|
+
if (discoverServicesRunnable != null) {
|
|
333
|
+
mainHandler.removeCallbacks(discoverServicesRunnable);
|
|
334
|
+
discoverServicesRunnable = null;
|
|
335
|
+
}
|
|
331
336
|
|
|
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;
|
|
337
|
+
for (Callback writeCallback: writeCallbacks) {
|
|
338
|
+
writeCallback.invoke("Device disconnected");
|
|
345
339
|
}
|
|
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);
|
|
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
|
-
|
|
381
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
362
|
+
for (Callback readCallback: readCallbacks) {
|
|
363
|
+
readCallback.invoke("Device disconnected");
|
|
364
|
+
}
|
|
365
|
+
readCallbacks.clear();
|
|
427
366
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
|
|
434
|
-
|
|
435
|
-
} else if (readCallback != null) {
|
|
459
|
+
readCallbacks.clear();
|
|
460
|
+
} else if (!readCallbacks.isEmpty()) {
|
|
436
461
|
final byte[] dataValue = copyOf(characteristic.getValue());
|
|
437
|
-
|
|
438
|
-
readCallback
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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,
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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 (
|
|
505
|
+
if (!registerNotifyCallbacks.isEmpty()) {
|
|
474
506
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
475
|
-
registerNotifyCallback
|
|
507
|
+
for (Callback registerNotifyCallback: registerNotifyCallbacks) {
|
|
508
|
+
registerNotifyCallback.invoke();
|
|
509
|
+
}
|
|
476
510
|
Log.d(BleManager.LOG_TAG, "onDescriptorWrite success");
|
|
477
511
|
} else {
|
|
478
|
-
|
|
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
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
518
|
+
registerNotifyCallbacks.clear();
|
|
519
|
+
} else {
|
|
520
|
+
Log.e(BleManager.LOG_TAG, "onDescriptorWrite with no callback");
|
|
521
|
+
}
|
|
486
522
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
523
|
+
completedCommand();
|
|
524
|
+
});
|
|
525
|
+
}
|
|
490
526
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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 (
|
|
532
|
+
if (!readRSSICallbacks.isEmpty()) {
|
|
497
533
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
498
534
|
updateRssi(rssi);
|
|
499
|
-
|
|
535
|
+
for (Callback readRSSICallback: readRSSICallbacks) {
|
|
536
|
+
readRSSICallback.invoke(null, rssi);
|
|
537
|
+
}
|
|
500
538
|
} else {
|
|
501
|
-
|
|
539
|
+
for (Callback readRSSICallback: readRSSICallbacks) {
|
|
540
|
+
readRSSICallback.invoke("Error reading RSSI status=" + status, null);
|
|
541
|
+
}
|
|
502
542
|
}
|
|
503
543
|
|
|
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
|
-
}
|
|
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
|
-
|
|
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);
|
|
618
|
+
for (Callback registerNotifyCallback: registerNotifyCallbacks) {
|
|
619
|
+
registerNotifyCallback.invoke("writeDescriptor failed for descriptor: " + descriptor.getUuid(), null);
|
|
606
620
|
}
|
|
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;
|
|
621
|
+
registerNotifyCallbacks.clear();
|
|
622
|
+
completedCommand();
|
|
645
623
|
}
|
|
646
624
|
}
|
|
647
625
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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
|
-
|
|
668
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
789
|
+
readRSSICallbacks.addLast(callback);
|
|
748
790
|
if (!gatt.readRemoteRssi()) {
|
|
749
|
-
|
|
750
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1006
|
+
requestMTUCallbacks.addLast(callback);
|
|
961
1007
|
if (!gatt.requestMtu(mtu)) {
|
|
962
|
-
|
|
963
|
-
|
|
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 (
|
|
1025
|
+
if (!requestMTUCallbacks.isEmpty()) {
|
|
978
1026
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
979
|
-
|
|
1027
|
+
for (Callback requestMTUCallback: requestMTUCallbacks) {
|
|
1028
|
+
requestMTUCallback.invoke(null, mtu);
|
|
1029
|
+
}
|
|
980
1030
|
} 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;
|
|
1031
|
+
for (Callback requestMTUCallback: requestMTUCallbacks) {
|
|
1032
|
+
requestMTUCallback.invoke("Error requesting MTU status = " + status, null);
|
|
1033
|
+
}
|
|
1009
1034
|
}
|
|
1010
|
-
}
|
|
1011
1035
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
}
|