-
Notifications
You must be signed in to change notification settings - Fork 7
/
UnityAndroidBLE.java
585 lines (448 loc) · 22 KB
/
UnityAndroidBLE.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
140
141
142
143
144
145
146
147
148
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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
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
577
578
579
580
581
582
583
584
585
package com.velorexe.unityandroidble;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.unity3d.player.UnityPlayer;
import com.velorexe.unityandroidble.connection.ConnectionRunnable;
import com.velorexe.unityandroidble.connection.ConnectionService;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class UnityAndroidBLE {
private static UnityAndroidBLE mInstance = null;
//MonoBehaviour GameObject to catch the BLE Messages
private static final String mUnityBLEReceiver = "BleAdapter";
//Command to send Bluetooth Low Energy data
private static final String mUnityBLECommand = "OnBleMessage";
//Command to send Unity Debug Logs
private static final String mUnityLogCommand = "LogMessage";
private static BluetoothAdapter mBluetoothAdapter = null;
public static BluetoothLeScanner mBluetoothLeScanner = null;
public static LeDeviceListAdapter mLeDeviceListAdapter = null;
private static Map<BluetoothDevice, BluetoothGatt> mLeGattServers = null;
private static Map<BluetoothDevice, ConnectionService> mConnectedServers = null;
public static boolean mScanning = false;
private Handler handler = new Handler();
private static Context mContext;
/**
* Gets called by Unity to Initialize the UnityAndroidBLE library
*
* @return UnityAndroidBLE manager
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static UnityAndroidBLE getInstance() {
if (mInstance == null) {
mInstance = new UnityAndroidBLE();
}
//Reset in case that it already exists
else {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.enable();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
mLeDeviceListAdapter = new LeDeviceListAdapter();
mLeGattServers = new HashMap<BluetoothDevice, BluetoothGatt>();
mConnectedServers = new HashMap<BluetoothDevice, ConnectionService>();
}
mContext = UnityPlayer.currentActivity.getApplicationContext();
//Checks to see if the device features Bluetooth Low Energy
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
BleObject obj = new BleObject("Initialized");
obj.setError("Device doesn't support Bluetooth Low Energy");
sendToUnity(obj);
}
//Enable Bluetooth if it isn't enabled
if (!mBluetoothAdapter.isEnabled())
mBluetoothAdapter.enable();
sendToUnity(new BleObject("Initialized"));
return mInstance;
}
@RequiresApi(api = Build.VERSION_CODES.M)
public static void checkPermissions(Context context, Activity activity) {
if (context.checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED
|| context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| context.checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}
public static void deInitialize() {
//Close all the connected Gatt Servers
for (Map.Entry<BluetoothDevice, BluetoothGatt> set : mLeGattServers.entrySet()) {
set.getValue().close();
}
mLeGattServers = null;
}
/**
* Constructor for UnityAndroidBLE
* Enables Bluetooth and creates instances for properties
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public UnityAndroidBLE() {
checkPermissions(UnityPlayer.currentActivity.getApplicationContext(), UnityPlayer.currentActivity);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.enable();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
mLeDeviceListAdapter = new LeDeviceListAdapter();
mLeGattServers = new HashMap<BluetoothDevice, BluetoothGatt>();
mConnectedServers = new HashMap<BluetoothDevice, ConnectionService>();
}
//region Scanning
/**
* Scans for Bluetooth Low Energy devices nearby
*
* @param scanPeriod the period of time the device scans for
*/
public void scanBleDevices(int scanPeriod) {
if (!mScanning) {
this.handler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothLeScanner.stopScan(bleScanCallback);
sendToUnity(new BleObject("FinishedDiscovering"));
}
}, scanPeriod);
mScanning = true;
mBluetoothLeScanner.startScan(bleScanCallback);
unityLog("Starting Scan");
return;
} else {
unityLog("BLE Manager is already scanning.");
}
}
/**
* Gets called when a new Bluetooth Low Energy device is discovered.
* Sends a message to Unity containing the DiscoveredDevice command,
* the device Address and a Name if the device contains one
*/
private ScanCallback bleScanCallback =
new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
if (mLeDeviceListAdapter.AddDevice(device)) {
BleObject obj = new BleObject("DiscoveredDevice");
obj.device = device.getAddress();
if (device.getName() != null) {
obj.name = device.getName();
}
sendToUnity(obj);
}
}
};
public void stopScanBleDevices() {
if (mScanning) {
mBluetoothLeScanner.stopScan(bleScanCallback);
mScanning = false;
}
}
//endregion
//region Connecting
/**
* Connects to a Bluetooth device with the given UUID
*
* @param deviceUuid the UUID of the device that the BluetoothAdapter should connect to
*/
public void connectToDevice(final String deviceUuid) {
BluetoothDevice device = mLeDeviceListAdapter.getItem(deviceUuid);
BleObject obj = new BleObject("StartConnection");
if (device != null && !mConnectedServers.containsKey(device)) {
obj.device = device.getAddress();
sendToUnity(obj);
ConnectionService service = new ConnectionService(this);
device.connectGatt(UnityPlayer.currentActivity.getApplicationContext(), true, service.gattCallback);
mConnectedServers.put(device, service);
} else {
obj.setError("BluetoothDevice hasn't been discovered yet");
sendToUnity(obj);
}
}
/**
* Passes through to Unity that the device is connected to the Gatt Server
*
* @param gattServer the GattServer that the device is connected to
*/
public void connectedToGattServer(BluetoothGatt gattServer) {
if (!mLeGattServers.containsKey(gattServer.getDevice())) {
mLeGattServers.put(gattServer.getDevice(), gattServer);
}
BleObject obj = new BleObject("ConnectedToGattServer");
obj.device = gattServer.getDevice().getAddress();
sendToUnity(obj);
}
public void disconnectDevice(String deviceAddress) {
BluetoothDevice device = mLeDeviceListAdapter.getItem(deviceAddress);
BluetoothGatt gatt = mLeGattServers.get(device);
BleObject obj = new BleObject("DisconnectedFromGattServer");
obj.device = device.getAddress();
if (gatt != null) {
gatt.close();
gatt.disconnect();
mConnectedServers.remove(mLeDeviceListAdapter.getItem(deviceAddress));
mLeGattServers.remove(device);
}
else {
obj.setError("Can't find connected device with address " + deviceAddress);
}
sendToUnity(obj);
}
/**
* Passes through to Unity that the device is disconnected from the Gatt Server
*
* @param gattServer the GattServer that the device lost connection to
*/
public void disconnectedFromGattServer(BluetoothGatt gattServer) {
disconnectDevice(gattServer.getDevice().getAddress());
}
//endregion
//region Discovering
/**
* Passes the found Services and Characteristics to Unity from the given Gatt
*
* @param gatt the Gatt device which the services have been found from
*/
public void discoveredService(BluetoothGatt gatt) {
List<BluetoothGattService> services = gatt.getServices();
for (int i = 0; i < services.size(); i++) {
BleObject obj = new BleObject("DiscoveredService");
obj.device = gatt.getDevice().getAddress();
obj.service = services.get(i).getUuid().toString();
sendToUnity(obj);
List<BluetoothGattCharacteristic> characteristics = services.get(i).getCharacteristics();
for (int j = 0; j < characteristics.size(); j++) {
obj.command = "DiscoveredCharacteristic";
obj.characteristic = characteristics.get(j).getUuid().toString();
sendToUnity(obj);
}
}
BleObject obj = new BleObject("DeviceConnected");
obj.device = gatt.getDevice().getAddress();
sendToUnity(obj);
}
//endregion
//region Reading
/**
* Subscribes to a given Characteristic
*
* @param device the device MAC Address
* @param service the UUID of the service under which the Characteristic is specified
* @param characteristic the UUID of the Characteristic to subscribe to
*/
public void subscribeToGattCharacteristic(String device, String service, String characteristic) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString("0000" + service + "-0000-1000-8000-00805f9b34fb");
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString("0000" + characteristic + "-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
UUID descriptorUUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(descriptorUUID);
gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gattDescriptor.setValue(new byte[]{0x01, 0x00});
gattServer.writeDescriptor(gattDescriptor);
BleObject obj = new BleObject("StartedSubscribingToCharacteristic");
if (gattServer.setCharacteristicNotification(gattCharacteristic, true)) {
obj.device = device;
obj.service = service;
obj.characteristic = characteristic;
} else {
obj.setError("Couldn't connect to the specified characteristic " + characteristic);
}
sendToUnity(obj);
}
public void unsubscribeFromGattCharacteristic(String device, String service, String characteristic) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString("0000" + service + "-0000-1000-8000-00805f9b34fb");
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString("0000" + characteristic + "-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
UUID descriptorUUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(descriptorUUID);
gattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
gattDescriptor.setValue(new byte[]{0x00, 0x00});
gattServer.writeDescriptor(gattDescriptor);
BleObject obj = new BleObject("StartedUnsubscribingFromCharacteristic");
if (gattServer.setCharacteristicNotification(gattCharacteristic, false)) {
obj.device = device;
obj.service = service;
obj.characteristic = characteristic;
} else {
obj.setError("Couldn't connect to the specified characteristic " + characteristic);
}
sendToUnity(obj);
}
public void subscribeToCustomGattCharacteristic(String device, String service, String characteristic) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString(service);
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString(characteristic);
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
UUID descriptorUUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(descriptorUUID);
gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gattDescriptor.setValue(new byte[]{0x01, 0x00});
gattServer.writeDescriptor(gattDescriptor);
BleObject obj = new BleObject("StartedSubscribingToCharacteristic");
if (gattServer.setCharacteristicNotification(gattCharacteristic, true)) {
obj.device = device;
obj.service = service;
obj.characteristic = characteristic;
} else {
obj.setError("Couldn't connect to the specified characteristic " + characteristic);
}
sendToUnity(obj);
}
public void unsubscribeFromCustomGattCharacteristic(String device, String service, String characteristic) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString(service);
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString(characteristic);
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
UUID descriptorUUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(descriptorUUID);
gattDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
gattDescriptor.setValue(new byte[]{0x01, 0x00});
gattServer.writeDescriptor(gattDescriptor);
BleObject obj = new BleObject("StartedUnsubscribingFromCharacteristic");
if (gattServer.setCharacteristicNotification(gattCharacteristic, false)) {
obj.device = device;
obj.service = service;
obj.characteristic = characteristic;
} else {
obj.setError("Couldn't connect to the specified characteristic " + characteristic);
}
sendToUnity(obj);
}
/**
* Passes the new value from the Characteristic to Unity
*
* @param gatt the Gatt device from which the Characteristic value has changed
* @param characteristic the Characteristic from which the value has changed
*/
public void characteristicValueChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
BleObject obj = new BleObject("CharacteristicValueChanged");
obj.device = gatt.getDevice().getAddress();
obj.service = characteristic.getService().getUuid().toString();
obj.characteristic = characteristic.getUuid().toString();
androidLog(Arrays.toString(data));
obj.base64Message = Base64.encodeToString(data, 0);
sendToUnity(obj);
}
public void readFromCharacteristic(String device, String service, String characteristic) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString("0000" + service + "-0000-1000-8000-00805f9b34fb");
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString("0000" + characteristic + "-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
gattServer.readCharacteristic(gattCharacteristic);
}
@SuppressLint("MissingPermission")
public void readFromCustomCharacteristic(String device, String service, String characteristic) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString(service);
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString(characteristic);
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
gattServer.readCharacteristic(gattCharacteristic);
}
//endregion
//region Writing
@SuppressLint("MissingPermission")
public void writeToGattCharacteristic(String device, String service, String characteristic, byte[] message) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString("0000" + service + "-0000-1000-8000-00805f9b34fb");
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString("0000" + characteristic + "-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
gattServer.readCharacteristic(gattCharacteristic);
}
public void writeToGattCharacteristic(String device, String service, String characteristic, String message) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString("0000" + service + "-0000-1000-8000-00805f9b34fb");
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString("0000" + characteristic + "-0000-1000-8000-00805f9b34fb");
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
gattCharacteristic.setValue(message);
gattServer.writeCharacteristic(gattCharacteristic);
}
public void writeToCustomGattCharacteristic(String device, String service, String characteristic, String message) {
byte[] decodedBytes = Base64.decode(message, 0);
androidLog(Arrays.toString(decodedBytes));
BluetoothDevice bDevice = mLeDeviceListAdapter.getItem(device);
BluetoothGatt gattServer = mLeGattServers.get(bDevice);
UUID serviceUUID = UUID.fromString(service);
BluetoothGattService gattService = gattServer.getService(serviceUUID);
UUID gattUUID = UUID.fromString(characteristic);
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(gattUUID);
gattCharacteristic.setValue(decodedBytes);
gattServer.writeCharacteristic(gattCharacteristic);
}
//endregion
//region Unity
/**
* Sends the given message to the Unity BLE Adapter
* @param message the message to be send to Unity
*/
// public static void sendToUnity(String message) {
// if(IS_ANDROID) {
// Log.i("UnityAndroidBLE", message);
// } else {
// UnityPlayer.UnitySendMessage(mUnityBLEReceiver, mUnityBLECommand, message);
// }
// }
/**
* Sends the given Byte array encoded to a string to Unity BLE Adapter
* @param data the data which needs to be send
* @param length the amount from the Byte array that needs to be send
*/
// public static void sendToUnity(byte[] data, int length) {
// UnityPlayer.UnitySendMessage(mUnityBLEReceiver, mUnityBLECommand, Base64.encodeToString(Arrays.copyOfRange(data, 0, length), 0));
// }
/**
* Sends the given message to Unity BLE Adapter to log inside the Unity stack trace
*
* @param message the message to be logged
*/
public static void unityLog(String message) {
UnityPlayer.UnitySendMessage(mUnityBLEReceiver, mUnityLogCommand, message);
}
public static void sendToUnity(BleObject obj) {
UnityPlayer.UnitySendMessage(mUnityBLEReceiver, mUnityBLECommand, obj.toJson());
}
/**
* Logs a message using Log.i
* This removes the clog of messages that Unity sends when using Debug.Log on Android
*
* @param message the message to log using Android log
*/
public static void androidLog(String message) {
Log.i("UnityAndroidBLE", message);
}
//endregion
}