From d640b79a4d7117c788d4950b0102bed4d81d292e Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Tue, 10 Dec 2024 10:58:42 +0100 Subject: [PATCH 01/10] Fix unfullfilled callback when gatt is null --- android/src/main/java/it/innove/Peripheral.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/android/src/main/java/it/innove/Peripheral.java b/android/src/main/java/it/innove/Peripheral.java index 72c74b3a..925fc752 100644 --- a/android/src/main/java/it/innove/Peripheral.java +++ b/android/src/main/java/it/innove/Peripheral.java @@ -921,10 +921,7 @@ private void nextCommand() { // Check if we still have a valid gatt object if (gatt == null) { - Log.d(BleManager.LOG_TAG, "Error, gatt is null"); - commandQueue.clear(); - commandQueueBusy = false; - return; + Log.d(BleManager.LOG_TAG, "Error, gatt is null. Let the next command fullfill its callback with an error"); } // Execute the next command in the queue From 4cf2f84fe0f83e804fbc2818d407906a89d850f2 Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Tue, 10 Dec 2024 11:14:32 +0100 Subject: [PATCH 02/10] Fix null pointer exception --- android/src/main/java/it/innove/Peripheral.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/src/main/java/it/innove/Peripheral.java b/android/src/main/java/it/innove/Peripheral.java index 925fc752..b4ad4332 100644 --- a/android/src/main/java/it/innove/Peripheral.java +++ b/android/src/main/java/it/innove/Peripheral.java @@ -1215,6 +1215,9 @@ private BluetoothGattCharacteristic findWritableCharacteristic(BluetoothGattServ writeProperty = BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE; } + if (service == null) { + throw new Exception("Service is null."); + } List characteristics = service.getCharacteristics(); for (BluetoothGattCharacteristic characteristic : characteristics) { if ((characteristic.getProperties() & writeProperty) != 0 From f43f054a971c59a703b0ca31e7154626a8004f2c Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Tue, 10 Dec 2024 19:25:50 +0100 Subject: [PATCH 03/10] More null checks for gatt --- android/src/main/java/it/innove/Peripheral.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/it/innove/Peripheral.java b/android/src/main/java/it/innove/Peripheral.java index b4ad4332..943ce29c 100644 --- a/android/src/main/java/it/innove/Peripheral.java +++ b/android/src/main/java/it/innove/Peripheral.java @@ -315,7 +315,7 @@ public void onConnectionStateChange(BluetoothGatt gatta, int status, final int n mainHandler.post(() -> { gatt = gatta; - if (status != BluetoothGatt.GATT_SUCCESS) { + if (gatt != null && status != BluetoothGatt.GATT_SUCCESS) { gatt.close(); } @@ -383,9 +383,12 @@ public void onConnectionStateChange(BluetoothGatt gatta, int status, final int n commandQueueBusy = false; connected = false; clearBuffers(); + + if (gatt != null) { + gatt.disconnect(); + gatt.close(); + } - gatt.disconnect(); - gatt.close(); gatt = null; sendDisconnectionEvent(device, BluetoothGatt.GATT_SUCCESS); } @@ -968,6 +971,10 @@ public void readRSSI(final Callback callback) { public void refreshCache(Callback callback) { enqueue(() -> { try { + if (gatt == null) { + throw new Exception("gatt is null"); + } + Method localMethod = gatt.getClass().getMethod("refresh", new Class[0]); boolean res = (Boolean) localMethod.invoke(gatt, new Object[0]); callback.invoke(null, res); From 3fe7ac4064e79539ba4b857a7cfb88fa3a331fa5 Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Tue, 10 Dec 2024 21:19:11 +0100 Subject: [PATCH 04/10] error handling in onServicesDiscovered. --- .../src/main/java/it/innove/Peripheral.java | 139 ++++++++++-------- 1 file changed, 81 insertions(+), 58 deletions(-) diff --git a/android/src/main/java/it/innove/Peripheral.java b/android/src/main/java/it/innove/Peripheral.java index 943ce29c..164545eb 100644 --- a/android/src/main/java/it/innove/Peripheral.java +++ b/android/src/main/java/it/innove/Peripheral.java @@ -12,6 +12,7 @@ import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; + import android.content.Context; import android.os.Build; import android.os.Handler; @@ -165,12 +166,10 @@ public void disconnect(final Callback callback, final boolean force) { } connectCallbacks.clear(); connected = false; - clearBuffers(); - commandQueue.clear(); - commandQueueBusy = false; if (gatt != null) { try { + resetQueuesAndBuffers(); gatt.disconnect(); if (force) { gatt.close(); @@ -297,15 +296,83 @@ public BluetoothDevice getDevice() { public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); mainHandler.post(() -> { - for (Callback retrieveServicesCallback : retrieveServicesCallbacks) { - WritableMap map = this.asWritableMap(gatt); - retrieveServicesCallback.invoke(null, map); + if (gatt == null) { + for (Callback retrieveServicesCallback : retrieveServicesCallbacks) { + retrieveServicesCallback.invoke("Error during service retrieval: gatt is null"); + } + } + else if (status == BluetoothGatt.GATT_SUCCESS) + { + for (Callback retrieveServicesCallback : retrieveServicesCallbacks) { + WritableMap map = this.asWritableMap(gatt); + retrieveServicesCallback.invoke(null, map); + } + } + else { + for (Callback retrieveServicesCallback : retrieveServicesCallbacks) { + retrieveServicesCallback.invoke("Error during service retrieval."); + } } retrieveServicesCallbacks.clear(); completedCommand(); }); } + public void errorAndClearAllCallbacks(final String errorMessage) { + + for (Callback writeCallback : writeCallbacks) { + writeCallback.invoke(errorMessage); + } + writeCallbacks.clear(); + + for (Callback retrieveServicesCallback : retrieveServicesCallbacks) { + retrieveServicesCallback.invoke(errorMessage); + } + retrieveServicesCallbacks.clear(); + + for (Callback readRSSICallback : readRSSICallbacks) { + readRSSICallback.invoke(errorMessage); + } + readRSSICallbacks.clear(); + + for (Callback registerNotifyCallback : registerNotifyCallbacks) { + registerNotifyCallback.invoke(errorMessage); + } + registerNotifyCallbacks.clear(); + + for (Callback requestMTUCallback : requestMTUCallbacks) { + requestMTUCallback.invoke(errorMessage); + } + requestMTUCallbacks.clear(); + + for (Callback readCallback : readCallbacks) { + readCallback.invoke(errorMessage); + } + readCallbacks.clear(); + + for (Callback readDescriptorCallback : readDescriptorCallbacks) { + readDescriptorCallback.invoke(errorMessage); + } + readDescriptorCallbacks.clear(); + + for (Callback callback : writeDescriptorCallbacks) { + callback.invoke(errorMessage); + } + writeDescriptorCallbacks.clear(); + + for (Callback connectCallback : connectCallbacks) { + connectCallback.invoke(errorMessage); + } + connectCallbacks.clear(); + } + + public void resetQueuesAndBuffers() { + writeQueue.clear(); + commandQueue.clear(); + commandQueueBusy = false; + connected = false; + clearBuffers(); + } @Override public void onConnectionStateChange(BluetoothGatt gatta, int status, final int newState) { @@ -333,57 +400,8 @@ public void onConnectionStateChange(BluetoothGatt gatta, int status, final int n } else if (newState == BluetoothProfile.STATE_DISCONNECTED || status != BluetoothGatt.GATT_SUCCESS) { - for (Callback writeCallback : writeCallbacks) { - writeCallback.invoke("Device disconnected"); - } - writeCallbacks.clear(); - - for (Callback retrieveServicesCallback : retrieveServicesCallbacks) { - retrieveServicesCallback.invoke("Device disconnected"); - } - retrieveServicesCallbacks.clear(); - - for (Callback readRSSICallback : readRSSICallbacks) { - readRSSICallback.invoke("Device disconnected"); - } - readRSSICallbacks.clear(); - - for (Callback registerNotifyCallback : registerNotifyCallbacks) { - registerNotifyCallback.invoke("Device disconnected"); - } - registerNotifyCallbacks.clear(); - - for (Callback requestMTUCallback : requestMTUCallbacks) { - requestMTUCallback.invoke("Device disconnected"); - } - requestMTUCallbacks.clear(); - - for (Callback readCallback : readCallbacks) { - readCallback.invoke("Device disconnected"); - } - readCallbacks.clear(); - - for (Callback readDescriptorCallback : readDescriptorCallbacks) { - readDescriptorCallback.invoke("Device disconnected"); - } - readDescriptorCallbacks.clear(); - - for (Callback callback : writeDescriptorCallbacks) { - callback.invoke("Device disconnected"); - } - writeDescriptorCallbacks.clear(); - - for (Callback connectCallback : connectCallbacks) { - connectCallback.invoke("Connection error"); - } - connectCallbacks.clear(); - - writeQueue.clear(); - commandQueue.clear(); - commandQueueBusy = false; - connected = false; - clearBuffers(); - + errorAndClearAllCallbacks("Device disconnected"); + resetQueuesAndBuffers(); if (gatt != null) { gatt.disconnect(); gatt.close(); @@ -894,7 +912,9 @@ private byte[] copyOf(byte[] source) { } private boolean enqueue(Runnable command) { + final boolean result = commandQueue.add(command); + if (result) { nextCommand(); } else { @@ -924,7 +944,10 @@ private void nextCommand() { // Check if we still have a valid gatt object if (gatt == null) { - Log.d(BleManager.LOG_TAG, "Error, gatt is null. Let the next command fullfill its callback with an error"); + Log.d(BleManager.LOG_TAG, "Error, gatt is null. Fill all callbacks with an error"); + errorAndClearAllCallbacks("Gatt is null"); + resetQueuesAndBuffers(); + return; } // Execute the next command in the queue From 5870d43df6ce6b2e157040c3bebe3eeea8f812fc Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Wed, 11 Dec 2024 10:52:14 +0100 Subject: [PATCH 05/10] fulfill all callback with an error when disconnect is called. --- android/src/main/java/it/innove/Peripheral.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/it/innove/Peripheral.java b/android/src/main/java/it/innove/Peripheral.java index 164545eb..b3afd42f 100644 --- a/android/src/main/java/it/innove/Peripheral.java +++ b/android/src/main/java/it/innove/Peripheral.java @@ -161,15 +161,12 @@ public void connect(final Callback callback, Activity activity, ReadableMap opti public void disconnect(final Callback callback, final boolean force) { mainHandler.post(() -> { - for (Callback connectCallback : connectCallbacks) { - connectCallback.invoke("Disconnect called before connect callback invoked"); - } - connectCallbacks.clear(); + errorAndClearAllCallbacks("Disconnect called before the command completed"); + resetQueuesAndBuffers(); connected = false; if (gatt != null) { try { - resetQueuesAndBuffers(); gatt.disconnect(); if (force) { gatt.close(); From ccfedc883970c1d3fc145344f11a2f125ad9ea20 Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Wed, 11 Dec 2024 11:04:14 +0100 Subject: [PATCH 06/10] stop calling stopScan on a null scanner [android simulator]. --- android/src/main/java/it/innove/DefaultScanManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/it/innove/DefaultScanManager.java b/android/src/main/java/it/innove/DefaultScanManager.java index b8205f87..e77ee92c 100644 --- a/android/src/main/java/it/innove/DefaultScanManager.java +++ b/android/src/main/java/it/innove/DefaultScanManager.java @@ -45,7 +45,9 @@ public void stopScan(Callback callback) { // update scanSessionId to prevent stopping next scan by running timeout thread scanSessionId.incrementAndGet(); - getBluetoothAdapter().getBluetoothLeScanner().stopScan(mScanCallback); + final BluetoothLeScanner scanner = getBluetoothAdapter().getBluetoothLeScanner(); + if (scanner) + getBluetoothAdapter().getBluetoothLeScanner().stopScan(mScanCallback); isScanning = false; callback.invoke(); } From c2c98da2b3f84c90c7914b2d9979c331cea7ac5b Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Wed, 11 Dec 2024 11:10:46 +0100 Subject: [PATCH 07/10] fix compilation --- android/src/main/java/it/innove/DefaultScanManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/it/innove/DefaultScanManager.java b/android/src/main/java/it/innove/DefaultScanManager.java index e77ee92c..83317dc7 100644 --- a/android/src/main/java/it/innove/DefaultScanManager.java +++ b/android/src/main/java/it/innove/DefaultScanManager.java @@ -7,6 +7,8 @@ import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; @@ -46,8 +48,8 @@ public void stopScan(Callback callback) { scanSessionId.incrementAndGet(); final BluetoothLeScanner scanner = getBluetoothAdapter().getBluetoothLeScanner(); - if (scanner) - getBluetoothAdapter().getBluetoothLeScanner().stopScan(mScanCallback); + if (scanner != null) + scanner.stopScan(mScanCallback); isScanning = false; callback.invoke(); } From 39dd6bafacba16eb8b582fffe7716c10b156ecd1 Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Wed, 11 Dec 2024 16:33:58 +0100 Subject: [PATCH 08/10] more stopScan null check. --- android/src/main/java/it/innove/DefaultScanManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/it/innove/DefaultScanManager.java b/android/src/main/java/it/innove/DefaultScanManager.java index 83317dc7..b872d7f9 100644 --- a/android/src/main/java/it/innove/DefaultScanManager.java +++ b/android/src/main/java/it/innove/DefaultScanManager.java @@ -177,7 +177,8 @@ public void run() { // check current scan session was not stopped if (scanSessionId.intValue() == currentScanSession) { if (btAdapter.getState() == BluetoothAdapter.STATE_ON) { - btAdapter.getBluetoothLeScanner().stopScan(mScanCallback); + final BluetoothLeScanner scanner = btAdapter.getBluetoothLeScanner(); + if (scanner) scanner.stopScan(mScanCallback); isScanning = false; } From 0ee6dffda2a1861973c918ce20542c0c77f49fa6 Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Fri, 20 Dec 2024 15:02:57 +0100 Subject: [PATCH 09/10] [disconnectPeripherals] cancel all callbacks and reset buffers --- android/src/main/java/it/innove/BleManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/src/main/java/it/innove/BleManager.java b/android/src/main/java/it/innove/BleManager.java index db16e93d..47f18714 100644 --- a/android/src/main/java/it/innove/BleManager.java +++ b/android/src/main/java/it/innove/BleManager.java @@ -732,6 +732,8 @@ private void disconnectPeripherals() { peripheral.disconnect(null, true); } } + peripheral.errorAndClearAllCallbacks("disconnected by BleManager"); + peripheral.resetQueuesAndBuffers(); } } } From c9ccdf110f3d6ffd1884318edb70cc2d79c6fd3a Mon Sep 17 00:00:00 2001 From: Matthieu Garrigues Date: Wed, 25 Dec 2024 00:59:43 +0100 Subject: [PATCH 10/10] Dummy fix. --- android/src/main/java/it/innove/BleManager.java | 4 ++-- android/src/main/java/it/innove/DefaultScanManager.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/it/innove/BleManager.java b/android/src/main/java/it/innove/BleManager.java index 74306b5a..26fa504b 100644 --- a/android/src/main/java/it/innove/BleManager.java +++ b/android/src/main/java/it/innove/BleManager.java @@ -767,9 +767,9 @@ private void disconnectPeripherals() { if (peripheral.isConnected()) { peripheral.disconnect(null, true); } + peripheral.errorAndClearAllCallbacks("disconnected by BleManager"); + peripheral.resetQueuesAndBuffers(); } - peripheral.errorAndClearAllCallbacks("disconnected by BleManager"); - peripheral.resetQueuesAndBuffers(); } } } diff --git a/android/src/main/java/it/innove/DefaultScanManager.java b/android/src/main/java/it/innove/DefaultScanManager.java index b872d7f9..d6c2ed89 100644 --- a/android/src/main/java/it/innove/DefaultScanManager.java +++ b/android/src/main/java/it/innove/DefaultScanManager.java @@ -178,7 +178,7 @@ public void run() { if (scanSessionId.intValue() == currentScanSession) { if (btAdapter.getState() == BluetoothAdapter.STATE_ON) { final BluetoothLeScanner scanner = btAdapter.getBluetoothLeScanner(); - if (scanner) scanner.stopScan(mScanCallback); + if (scanner != null) scanner.stopScan(mScanCallback); isScanning = false; }