bare-bluetooth-android
Advanced tools
| package to.holepunch.bare.bluetooth; | ||
| import android.bluetooth.le.AdvertiseData; | ||
| import android.bluetooth.le.AdvertiseSettings; | ||
| import android.bluetooth.le.BluetoothLeAdvertiser; | ||
| import android.os.ParcelUuid; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.UUID; | ||
| public final class AdvertiseHelper { | ||
| private final int advertiseMode; | ||
| private final boolean connectable; | ||
| private final boolean includeDeviceName; | ||
| private final List<UUID> serviceUuids = new ArrayList<>(); | ||
| public AdvertiseHelper(int advertiseMode, boolean connectable, boolean includeDeviceName) { | ||
| this.advertiseMode = advertiseMode; | ||
| this.connectable = connectable; | ||
| this.includeDeviceName = includeDeviceName; | ||
| } | ||
| public void | ||
| addServiceUuid(UUID uuid) { | ||
| serviceUuids.add(uuid); | ||
| } | ||
| public void | ||
| startAdvertising(BluetoothLeAdvertiser advertiser, android.bluetooth.le.AdvertiseCallback callback) { | ||
| AdvertiseSettings settings = new AdvertiseSettings.Builder() | ||
| .setAdvertiseMode(advertiseMode) | ||
| .setConnectable(connectable) | ||
| .build(); | ||
| AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder() | ||
| .setIncludeDeviceName(includeDeviceName); | ||
| for (UUID uuid : serviceUuids) { | ||
| dataBuilder.addServiceUuid(new ParcelUuid(uuid)); | ||
| } | ||
| advertiser.startAdvertising(settings, dataBuilder.build(), callback); | ||
| } | ||
| } |
| package to.holepunch.bare.bluetooth; | ||
| final class ErrorHelper { | ||
| static String | ||
| formatMessage(String prefix, Throwable error) { | ||
| String message = error.getMessage(); | ||
| return message == null || message.length() == 0 | ||
| ? prefix + ": " + error.getClass().getSimpleName() | ||
| : prefix + ": " + message; | ||
| } | ||
| } |
| package to.holepunch.bare.bluetooth; | ||
| import android.bluetooth.BluetoothGatt; | ||
| import android.bluetooth.BluetoothGattCharacteristic; | ||
| import android.bluetooth.BluetoothGattDescriptor; | ||
| import android.bluetooth.BluetoothGattService; | ||
| import java.util.UUID; | ||
| public final class GattServiceHelper { | ||
| private static final UUID CCCD_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); | ||
| private static final byte[] ENABLE_NOTIFICATION = {0x01, 0x00}; | ||
| private static final byte[] DISABLE_NOTIFICATION = {0x00, 0x00}; | ||
| public static BluetoothGattService | ||
| createService(UUID uuid, boolean isPrimary) { | ||
| return new BluetoothGattService( | ||
| uuid, | ||
| isPrimary ? BluetoothGattService.SERVICE_TYPE_PRIMARY : BluetoothGattService.SERVICE_TYPE_SECONDARY | ||
| ); | ||
| } | ||
| public static BluetoothGattCharacteristic | ||
| createCharacteristic(UUID uuid, int properties, int jsPermissions) { | ||
| int androidPermissions = 0; | ||
| if ((jsPermissions & 0x01) != 0) androidPermissions |= 0x01; | ||
| if ((jsPermissions & 0x02) != 0) androidPermissions |= 0x10; | ||
| if ((jsPermissions & 0x04) != 0) androidPermissions |= 0x02; | ||
| if ((jsPermissions & 0x08) != 0) androidPermissions |= 0x20; | ||
| BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic( | ||
| uuid, properties, androidPermissions | ||
| ); | ||
| if ((properties & 0x30) != 0) { | ||
| characteristic.addDescriptor(new BluetoothGattDescriptor(CCCD_UUID, 0x11)); | ||
| } | ||
| return characteristic; | ||
| } | ||
| public static boolean | ||
| subscribe(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { | ||
| boolean ok = gatt.setCharacteristicNotification(characteristic, true); | ||
| BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_UUID); | ||
| if (descriptor == null) return false; | ||
| descriptor.setValue(ENABLE_NOTIFICATION); | ||
| return gatt.writeDescriptor(descriptor) && ok; | ||
| } | ||
| public static boolean | ||
| unsubscribe(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { | ||
| boolean ok = gatt.setCharacteristicNotification(characteristic, false); | ||
| BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_UUID); | ||
| if (descriptor == null) return false; | ||
| descriptor.setValue(DISABLE_NOTIFICATION); | ||
| return gatt.writeDescriptor(descriptor) && ok; | ||
| } | ||
| } |
| package to.holepunch.bare.bluetooth; | ||
| import android.bluetooth.le.BluetoothLeScanner; | ||
| import android.bluetooth.le.ScanFilter; | ||
| import android.bluetooth.le.ScanSettings; | ||
| import android.os.ParcelUuid; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.UUID; | ||
| public final class ScanHelper { | ||
| private final int scanMode; | ||
| private final List<UUID> uuids = new ArrayList<>(); | ||
| public ScanHelper(int scanMode) { | ||
| this.scanMode = scanMode; | ||
| } | ||
| public void | ||
| addServiceUuid(UUID uuid) { | ||
| uuids.add(uuid); | ||
| } | ||
| public void | ||
| startScan(BluetoothLeScanner scanner, android.bluetooth.le.ScanCallback callback) { | ||
| ScanSettings settings = new ScanSettings.Builder() | ||
| .setScanMode(scanMode) | ||
| .build(); | ||
| List<ScanFilter> filters = null; | ||
| if (!uuids.isEmpty()) { | ||
| filters = new ArrayList<>(); | ||
| for (UUID uuid : uuids) { | ||
| filters.add( | ||
| new ScanFilter.Builder() | ||
| .setServiceUuid(new ParcelUuid(uuid)) | ||
| .build() | ||
| ); | ||
| } | ||
| } | ||
| scanner.startScan(filters, settings, callback); | ||
| } | ||
| } |
| package to.holepunch.bare.bluetooth; | ||
| final class ThreadHelper { | ||
| private static final long JOIN_TIMEOUT_MS = 1000; | ||
| static void | ||
| join(Thread thread) { | ||
| if (thread != null && thread != Thread.currentThread()) { | ||
| try { | ||
| thread.join(JOIN_TIMEOUT_MS); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| } | ||
| } | ||
| } |
+5
-0
@@ -43,8 +43,13 @@ cmake_minimum_required(VERSION 4.0) | ||
| lib/ScanCallback.java | ||
| lib/ScanHelper.java | ||
| lib/GattCallback.java | ||
| lib/GattServiceHelper.java | ||
| lib/GattServerCallback.java | ||
| lib/AdvertiseCallback.java | ||
| lib/AdvertiseHelper.java | ||
| lib/L2capConnector.java | ||
| lib/L2capReader.java | ||
| lib/L2capAcceptor.java | ||
| lib/ErrorHelper.java | ||
| lib/ThreadHelper.java | ||
| INCLUDE_JARS | ||
@@ -51,0 +56,0 @@ ${android_jar} |
@@ -11,4 +11,2 @@ package to.holepunch.bare.bluetooth; | ||
| public final class L2capAcceptor implements Runnable { | ||
| private static final long JOIN_TIMEOUT_MS = 1000; | ||
| private final BluetoothServerSocket serverSocket; | ||
@@ -44,15 +42,7 @@ private final long nativeId; | ||
| } catch (IOException | RuntimeException e) { | ||
| nativeOnError(nativeId, psm, errorMessage("L2CAP accept close failed", e)); | ||
| nativeOnError(nativeId, psm, ErrorHelper.formatMessage("L2CAP accept close failed", e)); | ||
| } | ||
| Thread t = thread; | ||
| ThreadHelper.join(thread); | ||
| if (t != null && t != Thread.currentThread()) { | ||
| try { | ||
| t.join(JOIN_TIMEOUT_MS); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| } | ||
| for (BluetoothSocket socket : acceptedSockets.values()) { | ||
@@ -95,3 +85,3 @@ closeAcceptedSocket(socket); | ||
| if (!stopped) { | ||
| nativeOnError(nativeId, psm, errorMessage("L2CAP accept failed", e)); | ||
| nativeOnError(nativeId, psm, ErrorHelper.formatMessage("L2CAP accept failed", e)); | ||
| } | ||
@@ -109,14 +99,6 @@ | ||
| } catch (IOException | RuntimeException e) { | ||
| nativeOnError(nativeId, psm, errorMessage("Accepted L2CAP socket close failed", e)); | ||
| nativeOnError(nativeId, psm, ErrorHelper.formatMessage("Accepted L2CAP socket close failed", e)); | ||
| } | ||
| } | ||
| private static String | ||
| errorMessage(String prefix, Throwable error) { | ||
| String message = error.getMessage(); | ||
| return message == null || message.length() == 0 | ||
| ? prefix + ": " + error.getClass().getSimpleName() | ||
| : prefix + ": " + message; | ||
| } | ||
| private static native void | ||
@@ -123,0 +105,0 @@ nativeOnAccepted(long nativeId, int psm, int socketId); |
@@ -8,4 +8,2 @@ package to.holepunch.bare.bluetooth; | ||
| public final class L2capConnector implements Runnable { | ||
| private static final long JOIN_TIMEOUT_MS = 1000; | ||
| private final BluetoothSocket socket; | ||
@@ -39,11 +37,3 @@ private final long nativeId; | ||
| Thread t = thread; | ||
| if (t != null && t != Thread.currentThread()) { | ||
| try { | ||
| t.join(JOIN_TIMEOUT_MS); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| } | ||
| ThreadHelper.join(thread); | ||
| } | ||
@@ -67,3 +57,3 @@ | ||
| } catch (IOException | RuntimeException e) { | ||
| error = cancelled ? "L2CAP connect cancelled" : errorMessage("L2CAP connect failed", e); | ||
| error = cancelled ? "L2CAP connect cancelled" : ErrorHelper.formatMessage("L2CAP connect failed", e); | ||
| } | ||
@@ -88,9 +78,2 @@ | ||
| private static String | ||
| errorMessage(String prefix, Throwable error) { | ||
| String message = error.getMessage(); | ||
| return message == null || message.length() == 0 | ||
| ? prefix + ": " + error.getClass().getSimpleName() | ||
| : prefix + ": " + message; | ||
| } | ||
| } |
+2
-20
@@ -10,4 +10,2 @@ package to.holepunch.bare.bluetooth; | ||
| public final class L2capReader implements Runnable { | ||
| private static final long JOIN_TIMEOUT_MS = 1000; | ||
| private final BluetoothSocket socket; | ||
@@ -39,11 +37,3 @@ private final long nativeId; | ||
| Thread t = thread; | ||
| if (t != null && t != Thread.currentThread()) { | ||
| try { | ||
| t.join(JOIN_TIMEOUT_MS); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| } | ||
| ThreadHelper.join(thread); | ||
| } | ||
@@ -77,3 +67,3 @@ | ||
| } catch (IOException | RuntimeException e) { | ||
| if (!stopped) nativeOnError(nativeId, errorMessage("Read error", e)); | ||
| if (!stopped) nativeOnError(nativeId, ErrorHelper.formatMessage("Read error", e)); | ||
| } finally { | ||
@@ -94,10 +84,2 @@ closeSocket(); | ||
| private static String | ||
| errorMessage(String prefix, Throwable error) { | ||
| String message = error.getMessage(); | ||
| return message == null || message.length() == 0 | ||
| ? prefix + ": " + error.getClass().getSimpleName() | ||
| : prefix + ": " + message; | ||
| } | ||
| private static native void | ||
@@ -104,0 +86,0 @@ nativeOnOpen(long nativeId); |
+1
-1
| { | ||
| "name": "bare-bluetooth-android", | ||
| "version": "0.5.0", | ||
| "version": "0.5.1", | ||
| "description": "Android Bluetooth bindings for Bare", | ||
@@ -5,0 +5,0 @@ "addon": true, |
+26
-0
@@ -332,2 +332,18 @@ # bare-bluetooth-android | ||
| #### `event: 'connecting'` | ||
| Emitted when a central is connecting. The listener receives `deviceAddress`. | ||
| #### `event: 'connected'` | ||
| Emitted when a central has connected. The listener receives `deviceAddress`. | ||
| #### `event: 'disconnecting'` | ||
| Emitted when a central is disconnecting. The listener receives `deviceAddress`. | ||
| #### `event: 'disconnected'` | ||
| Emitted when a central has disconnected. The listener receives `deviceAddress`. | ||
| #### `event: 'notifySent'` | ||
@@ -337,2 +353,12 @@ | ||
| #### `Server.CONNECTION_STATE_DISCONNECTED` | ||
| #### `Server.CONNECTION_STATE_CONNECTING` | ||
| #### `Server.CONNECTION_STATE_CONNECTED` | ||
| #### `Server.CONNECTION_STATE_DISCONNECTING` | ||
| Connection state constants matching `BluetoothProfile.STATE_*`. | ||
| #### `Server.PROPERTY_READ` | ||
@@ -339,0 +365,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
52
10.64%1576
6.85%485
5.66%4472558
-4.6%