From f2197c9ca9c6684a6df1d55270c362dcc2248586 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 7 Oct 2018 09:24:37 +0200 Subject: [PATCH 01/14] Do gatt connect on main thread --- .../core/bluetooth/BluetoothCommunication.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index a5360d46..8abe809a 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -476,15 +476,20 @@ public abstract class BluetoothCommunication { private void startLeScanForDevice(final String hwAddress) { leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override - public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { + public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { Timber.d("Found LE device %s [%s]", device.getName(), device.getAddress()); if (!device.getAddress().equals(hwAddress)) { return; } - synchronized (lock) { - stopLeScan(); - connectGatt(device); - } + handler.post(new Runnable() { + @Override + public void run() { + if (leScanCallback != null) { + stopLeScan(); + connectGatt(device); + } + } + }); } }; From c381f48aa9dffdf3bcc0f97786b97f208c0178f4 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 7 Oct 2018 19:06:04 +0200 Subject: [PATCH 02/14] Try to trigger release on all branches --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2a05f78c..8967f482 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,4 +70,5 @@ deploy: overwrite: true target_commitish: $TRAVIS_COMMIT on: + all_branches: true tags: false From 8d47f9b968f21663e2eb3b7f7c6abe8bf7b47b0b Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 7 Oct 2018 22:26:40 +0200 Subject: [PATCH 03/14] Try to run all ble commands on the main thread --- .../bluetooth/BluetoothCommunication.java | 350 +++++++++--------- 1 file changed, 175 insertions(+), 175 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index 8abe809a..e6d42d0a 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -80,7 +80,6 @@ public abstract class BluetoothCommunication { private Queue> descriptorRequestQueue; private Queue> characteristicRequestQueue; private boolean openRequest; - private final Object lock = new Object(); public BluetoothCommunication(Context context) { @@ -240,10 +239,8 @@ public abstract class BluetoothCommunication { * @param btMachineState the machine state that should be set. */ protected void setBtMachineState(BT_MACHINE_STATE btMachineState) { - synchronized (lock) { - this.btMachineState = btMachineState; - handleRequests(); - } + this.btMachineState = btMachineState; + handleRequests(); } /** @@ -254,13 +251,11 @@ public abstract class BluetoothCommunication { * @param bytes the bytes that should be write */ protected void writeBytes(UUID service, UUID characteristic, byte[] bytes) { - synchronized (lock) { - characteristicRequestQueue.add( - new GattObjectValue<>( - bluetoothGatt.getService(service).getCharacteristic(characteristic), - bytes)); - handleRequests(); - } + characteristicRequestQueue.add( + new GattObjectValue<>( + bluetoothGatt.getService(service).getCharacteristic(characteristic), + bytes)); + handleRequests(); } /** @@ -301,13 +296,11 @@ public abstract class BluetoothCommunication { bluetoothGatt.getService(service).getCharacteristic(characteristic); bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true); - synchronized (lock) { - descriptorRequestQueue.add( - new GattObjectValue<>( - gattCharacteristic.getDescriptor(descriptor), - BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)); - handleRequests(); - } + descriptorRequestQueue.add( + new GattObjectValue<>( + gattCharacteristic.getDescriptor(descriptor), + BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)); + handleRequests(); } catch (Exception e) { Timber.e(e); @@ -328,13 +321,11 @@ public abstract class BluetoothCommunication { bluetoothGatt.getService(service).getCharacteristic(characteristic); bluetoothGatt.setCharacteristicNotification(gattCharacteristic, true); - synchronized (lock) { - descriptorRequestQueue.add( - new GattObjectValue<>( - gattCharacteristic.getDescriptor(descriptor), - BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)); - handleRequests(); - } + descriptorRequestQueue.add( + new GattObjectValue<>( + gattCharacteristic.getDescriptor(descriptor), + BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)); + handleRequests(); } catch (Exception e) { Timber.e(e); @@ -354,13 +345,11 @@ public abstract class BluetoothCommunication { bluetoothGatt.getService(service).getCharacteristic(characteristic); bluetoothGatt.setCharacteristicNotification(gattCharacteristic, false); - synchronized (lock) { - descriptorRequestQueue.add( - new GattObjectValue<>( - gattCharacteristic.getDescriptor(descriptor), - BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)); - handleRequests(); - } + descriptorRequestQueue.add( + new GattObjectValue<>( + gattCharacteristic.getDescriptor(descriptor), + BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)); + handleRequests(); } /** @@ -415,6 +404,8 @@ public abstract class BluetoothCommunication { * @param hwAddress the Bluetooth address to connect to */ public void connect(String hwAddress) { + disconnect(false); + logBluetoothStatus(); // Some good tips to improve BLE connections: @@ -481,6 +472,7 @@ public abstract class BluetoothCommunication { if (!device.getAddress().equals(hwAddress)) { return; } + // Stop scan and connect to the device on the main thread handler.post(new Runnable() { @Override public void run() { @@ -496,14 +488,13 @@ public abstract class BluetoothCommunication { Timber.d("Starting LE scan for device [%s]", hwAddress); btAdapter.startLeScan(leScanCallback); + // Stop scan and try to connect to the device directly if the device isn't found in time handler.postAtTime(new Runnable() { @Override public void run() { Timber.d("Device not found in LE scan, connecting directly"); - synchronized (lock) { - stopLeScan(); - connectGatt(hwAddress); - } + stopLeScan(); + connectGatt(hwAddress); } }, leScanCallback, SystemClock.uptimeMillis() + LE_SCAN_TIMEOUT_MS); } @@ -521,42 +512,39 @@ public abstract class BluetoothCommunication { * Disconnect from a Bluetooth device */ public void disconnect(boolean doCleanup) { - synchronized (lock) { - stopLeScan(); + stopLeScan(); - if (bluetoothGatt == null) { - return; - } - - Timber.i("Disconnecting%s", doCleanup ? " (with cleanup)" : ""); - - handler.removeCallbacksAndMessages(null); - callbackBtHandler = null; - - if (doCleanup) { - if (btMachineState != BT_MACHINE_STATE.BT_CLEANUP_STATE) { - setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); - nextMachineStateStep(); - } - handler.post(new Runnable() { - @Override - public void run() { - synchronized (lock) { - if (openRequest) { - handler.postDelayed(this, 10); - } else { - bluetoothGatt.close(); - bluetoothGatt = null; - } - } - } - }); - } - else { - bluetoothGatt.close(); - bluetoothGatt = null; - } + if (bluetoothGatt == null) { + return; } + + Timber.i("Disconnecting%s", doCleanup ? " (with cleanup)" : ""); + + if (doCleanup) { + if (btMachineState != BT_MACHINE_STATE.BT_CLEANUP_STATE) { + setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); + nextMachineStateStep(); + } + handler.post(new Runnable() { + @Override + public void run() { + if (openRequest) { + handler.postDelayed(this, 10); + } else { + bluetoothGatt.close(); + bluetoothGatt = null; + handler.removeCallbacksAndMessages(null); + } + } + }); + } + else { + bluetoothGatt.close(); + bluetoothGatt = null; + } + + handler.removeCallbacksAndMessages(null); + callbackBtHandler = null; } /** @@ -586,49 +574,48 @@ public abstract class BluetoothCommunication { } private void handleRequests() { - synchronized (lock) { - // check for pending request - if (openRequest) { - Timber.d("Request pending (queue %d %d)", - descriptorRequestQueue.size(), characteristicRequestQueue.size()); - return; // yes, do nothing - } - - // handle descriptor requests first - GattObjectValue descriptor = descriptorRequestQueue.poll(); - if (descriptor != null) { - descriptor.gattObject.setValue(descriptor.value); - - Timber.d("Write descriptor %s: %s (queue: %d %d)", - descriptor.gattObject.getUuid(), byteInHex(descriptor.gattObject.getValue()), - descriptorRequestQueue.size(), characteristicRequestQueue.size()); - if (!bluetoothGatt.writeDescriptor(descriptor.gattObject)) { - Timber.e("Failed to initiate write of descriptor %s", - descriptor.gattObject.getUuid()); - } - openRequest = true; - return; - } - - // handle characteristics requests second - GattObjectValue characteristic = characteristicRequestQueue.poll(); - if (characteristic != null) { - characteristic.gattObject.setValue(characteristic.value); - - Timber.d("Write characteristic %s: %s (queue: %d %d)", - characteristic.gattObject.getUuid(), byteInHex(characteristic.gattObject.getValue()), - descriptorRequestQueue.size(), characteristicRequestQueue.size()); - if (!bluetoothGatt.writeCharacteristic(characteristic.gattObject)) { - Timber.e("Failed to initiate write of characteristic %s", - characteristic.gattObject.getUuid()); - } - openRequest = true; - return; - } - - // After every command was executed, continue with the next step - nextMachineStateStep(); + // check for pending request + if (openRequest) { + Timber.d("Request pending (queue %d %d)", + descriptorRequestQueue.size(), characteristicRequestQueue.size()); + return; // yes, do nothing } + + // handle descriptor requests first + GattObjectValue descriptor = descriptorRequestQueue.poll(); + if (descriptor != null) { + descriptor.gattObject.setValue(descriptor.value); + + Timber.d("Write descriptor %s: %s (queue: %d %d)", + descriptor.gattObject.getUuid(), byteInHex(descriptor.gattObject.getValue()), + descriptorRequestQueue.size(), characteristicRequestQueue.size()); + if (!bluetoothGatt.writeDescriptor(descriptor.gattObject)) { + Timber.e("Failed to initiate write of descriptor %s", + descriptor.gattObject.getUuid()); + } + openRequest = true; + return; + } + + // handle characteristics requests second + GattObjectValue characteristic = characteristicRequestQueue.poll(); + if (characteristic != null) { + characteristic.gattObject.setValue(characteristic.value); + + Timber.d("Write characteristic %s: %s (type: %d; queue: %d %d)", + characteristic.gattObject.getUuid(), byteInHex(characteristic.gattObject.getValue()), + characteristic.gattObject.getWriteType(), + descriptorRequestQueue.size(), characteristicRequestQueue.size()); + if (!bluetoothGatt.writeCharacteristic(characteristic.gattObject)) { + Timber.e("Failed to initiate write of characteristic %s", + characteristic.gattObject.getUuid()); + } + openRequest = true; + return; + } + + // After every command was executed, continue with the next step + nextMachineStateStep(); } /** @@ -640,34 +627,42 @@ public abstract class BluetoothCommunication { Timber.d("onConnectionStateChange: status=%d, newState=%d", status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { - synchronized (lock) { - stopLeScan(); - } + handler.post(new Runnable() { + @Override + public void run() { + stopLeScan(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - gatt.readPhy(); - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + gatt.readPhy(); + } - connectionEstablished = true; - setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED); + connectionEstablished = true; + setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED); + } + }); - try { - Thread.sleep(1000); - } - catch (Exception e) { - // Empty - } - if (!gatt.discoverServices()) { - Timber.e("Could not start service discovery"); - setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); - disconnect(false); - } + // Wait a short while after connecting before scanning for services + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (!gatt.discoverServices()) { + Timber.e("Could not start service discovery"); + setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); + disconnect(false); + } + } + }, 1000); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - setBtStatus(connectionEstablished - ? BT_STATUS_CODE.BT_CONNECTION_LOST - : BT_STATUS_CODE.BT_NO_DEVICE_FOUND); - disconnect(false); + handler.post(new Runnable() { + @Override + public void run() { + setBtStatus(connectionEstablished + ? BT_STATUS_CODE.BT_CONNECTION_LOST + : BT_STATUS_CODE.BT_NO_DEVICE_FOUND); + disconnect(false); + } + }); } } @@ -677,34 +672,35 @@ public abstract class BluetoothCommunication { status, gatt.getServices().size()); if (gatt.getServices().isEmpty()) { - setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "No services found"); - disconnect(false); + handler.post(new Runnable() { + @Override + public void run() { + setBtStatus(BT_STATUS_CODE.BT_UNEXPECTED_ERROR, "No services found"); + disconnect(false); + } + }); return; } - synchronized (lock) { - cmdStepNr = 0; - initStepNr = 0; - cleanupStepNr = 0; + // Sleeping a while after discovering services fixes connection problems. + // See https://github.com/NordicSemiconductor/Android-DFU-Library/issues/10 + // for some technical background. + handler.postDelayed(new Runnable() { + @Override + public void run() { + cmdStepNr = 0; + initStepNr = 0; + cleanupStepNr = 0; - // Clear from possible previous setups - characteristicRequestQueue = new LinkedList<>(); - descriptorRequestQueue = new LinkedList<>(); - openRequest = false; - } + // Clear from possible previous setups + characteristicRequestQueue = new LinkedList<>(); + descriptorRequestQueue = new LinkedList<>(); + openRequest = false; - try { - // Sleeping a while after discovering services fixes connection problems. - // See https://github.com/NordicSemiconductor/Android-DFU-Library/issues/10 - // for some technical background. - Thread.sleep(1000); - } - catch (Exception e) { - // Empty - } - - // Start the state machine - setBtMachineState(BT_MACHINE_STATE.BT_INIT_STATE); + // Start the state machine + setBtMachineState(BT_MACHINE_STATE.BT_INIT_STATE); + } + }, 1000); } @Override @@ -723,10 +719,8 @@ public abstract class BluetoothCommunication { handler.postDelayed(new Runnable() { @Override public void run() { - synchronized (lock) { - openRequest = false; - handleRequests(); - } + openRequest = false; + handleRequests(); } }, 60); } @@ -746,27 +740,33 @@ public abstract class BluetoothCommunication { } @Override - public void onCharacteristicRead(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, - int status) { + public void onCharacteristicRead(final BluetoothGatt gatt, + final BluetoothGattCharacteristic characteristic, + final int status) { Timber.d("onCharacteristicRead %s (status=%d): %s", characteristic.getUuid(), status, byteInHex(characteristic.getValue())); - synchronized (lock) { - onBluetoothDataRead(gatt, characteristic, status); - postDelayedHandleRequests(); - } + handler.post(new Runnable() { + @Override + public void run() { + onBluetoothDataRead(gatt, characteristic, status); + } + }); + postDelayedHandleRequests(); } @Override - public void onCharacteristicChanged(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic) { + public void onCharacteristicChanged(final BluetoothGatt gatt, + final BluetoothGattCharacteristic characteristic) { Timber.d("onCharacteristicChanged %s: %s", characteristic.getUuid(), byteInHex(characteristic.getValue())); - synchronized (lock) { - onBluetoothDataChange(gatt, characteristic); - } + handler.post(new Runnable() { + @Override + public void run() { + onBluetoothDataChange(gatt, characteristic); + } + }); } @Override From c03d8c7601cd0f3cf2b14296d2cc1cad6dc29472 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 7 Oct 2018 22:27:01 +0200 Subject: [PATCH 04/14] Log write type in debug driver --- .../core/bluetooth/BluetoothDebug.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothDebug.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothDebug.java index 6d54bfa2..f5bdd0d7 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothDebug.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothDebug.java @@ -74,13 +74,28 @@ public class BluetoothDebug extends BluetoothCommunication { return names.substring(0, names.length() - 2); } + private String writeTypeToString(int type) { + if (type == BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) { + return "no response"; + } + if (type == BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) { + return "default"; + } + if (type == BluetoothGattCharacteristic.WRITE_TYPE_SIGNED) { + return "signed"; + } + return String.format("unknown type %d", type); + } + private void logService(BluetoothGattService service, boolean included) { Timber.d("Service %s%s", service.getUuid(), included ? " (included)" : ""); for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { - Timber.d("|- characteristic %s (instance %d): %s (permissions=0x%x)", + Timber.d("|- characteristic %s (instance %d): %s, write type: %s (permissions=0x%x)", characteristic.getUuid(), characteristic.getInstanceId(), - propertiesToString(characteristic.getProperties()), characteristic.getPermissions()); + propertiesToString(characteristic.getProperties()), + writeTypeToString(characteristic.getWriteType()), + characteristic.getPermissions()); byte[] value = characteristic.getValue(); if (value != null && value.length > 0) { Timber.d("|--> value: %s (%s)", byteInHex(value), From 2f1b7663b768af4174fa128ffaba2e56a8380092 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Mon, 8 Oct 2018 21:12:34 +0200 Subject: [PATCH 05/14] Descriptor 0x2902 is the one that should be used to enable notifications Both according to https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml and the bt snoop log in #319 (from @Alejandro131) --- .../openscale/core/bluetooth/BluetoothBeurerSanitas.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java index 2b646a6d..de8e691d 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java @@ -78,10 +78,8 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication { private static final UUID SERVICE_CHANGED = UUID.fromString("00002A05-0000-1000-8000-00805F9B34FB"); - private static final UUID CLIENT_CHARACTERISTICS_CONFIGURATION_BEURER = + private static final UUID CLIENT_CHARACTERISTICS_CONFIGURATION = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB"); - private static final UUID CLIENT_CHARACTERISTICS_CONFIGURATION_SANITAS = - UUID.fromString("00002901-0000-1000-8000-00805F9B34FB"); private static final UUID CUSTOM_SERVICE_1 = UUID.fromString("0000FFE0-0000-1000-8000-00805F9B34FB"); @@ -156,10 +154,7 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication { seenUsers = new TreeSet<>(); // Setup notification - UUID clientCharacteristicsConfiguration = deviceType == DeviceType.SANITAS_SBF70_70 - ? CLIENT_CHARACTERISTICS_CONFIGURATION_SANITAS - : CLIENT_CHARACTERISTICS_CONFIGURATION_BEURER; - setNotificationOn(CUSTOM_SERVICE_1, CUSTOM_CHARACTERISTIC_WEIGHT, clientCharacteristicsConfiguration); + setNotificationOn(CUSTOM_SERVICE_1, CUSTOM_CHARACTERISTIC_WEIGHT, CLIENT_CHARACTERISTICS_CONFIGURATION); break; case 1: // Say "Hello" to the scale From 960c7e8f3041d59e91b4ff885f3c7cf28692ade9 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Mon, 8 Oct 2018 21:36:19 +0200 Subject: [PATCH 06/14] Wait a bit between stopping LE scan and connecting To see if it helps @ReidarHH in #335 --- .../bluetooth/BluetoothCommunication.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index e6d42d0a..6d1f903f 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -427,7 +427,7 @@ public abstract class BluetoothCommunication { } else { Timber.d("No coarse location permission, connecting without LE scan"); - connectGatt(hwAddress); + connectGatt(btAdapter.getRemoteDevice(hwAddress)); } } @@ -460,8 +460,16 @@ public abstract class BluetoothCommunication { } } - private void connectGatt(String hwAddress) { - connectGatt(btAdapter.getRemoteDevice(hwAddress)); + private void stopLeScanAndConnectGatt(final BluetoothDevice device) { + stopLeScan(); + + // Delay the call to connectGatt to let things settle a bit + handler.postDelayed(new Runnable() { + @Override + public void run() { + connectGatt(device); + } + }, 500); } private void startLeScanForDevice(final String hwAddress) { @@ -476,27 +484,28 @@ public abstract class BluetoothCommunication { handler.post(new Runnable() { @Override public void run() { + // Check that callback != null in case the same device is found multiple times + // and thus multiple calls to connect to it are queued. Only the first will + // have callback != null. if (leScanCallback != null) { - stopLeScan(); - connectGatt(device); + stopLeScanAndConnectGatt(device); } } }); } }; - Timber.d("Starting LE scan for device [%s]", hwAddress); - btAdapter.startLeScan(leScanCallback); - // Stop scan and try to connect to the device directly if the device isn't found in time handler.postAtTime(new Runnable() { @Override public void run() { Timber.d("Device not found in LE scan, connecting directly"); - stopLeScan(); - connectGatt(hwAddress); + stopLeScanAndConnectGatt(btAdapter.getRemoteDevice(hwAddress)); } }, leScanCallback, SystemClock.uptimeMillis() + LE_SCAN_TIMEOUT_MS); + + Timber.d("Starting LE scan for device [%s]", hwAddress); + btAdapter.startLeScan(leScanCallback); } private void stopLeScan() { @@ -515,6 +524,8 @@ public abstract class BluetoothCommunication { stopLeScan(); if (bluetoothGatt == null) { + // Could be a pending connectGatt waiting + handler.removeCallbacksAndMessages(null); return; } From bbbb91ecdd5d3e7088b7ec6b95212f60c34a9bc0 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Tue, 9 Oct 2018 21:30:23 +0200 Subject: [PATCH 07/14] Disconnect after setting initial weight for new user To see if it helps the problem reported by @Alejandro131 in #335. --- .../openscale/core/bluetooth/BluetoothBeurerSanitas.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java index de8e691d..3e0f4aa8 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothBeurerSanitas.java @@ -481,6 +481,12 @@ public class BluetoothBeurerSanitas extends BluetoothCommunication { (byte) (data[2] & 0xFF), (byte) (data[3] & 0xFF), }); + if (currentScaleUserId == 0) { + Timber.i("Initial weight set; disconnecting..."); + setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); + return; + } + return; } From 2cc98f4416cb089ba0bc5f5600602e77d1da8543 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Tue, 9 Oct 2018 21:41:02 +0200 Subject: [PATCH 08/14] Try connectGatt with autoConnect = true if it fails with autoConnect = false See #335 --- .../core/bluetooth/BluetoothCommunication.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index 6d1f903f..dc26de07 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -56,6 +56,7 @@ public abstract class BluetoothCommunication { private Handler callbackBtHandler; private Handler handler; + private static boolean autoConnect = false; private BluetoothGatt bluetoothGatt; private boolean connectionEstablished; private BluetoothGattCallback gattCallback; @@ -449,14 +450,15 @@ public abstract class BluetoothCommunication { } private void connectGatt(BluetoothDevice device) { - Timber.i("Connecting to [%s] (driver: %s)", device.getAddress(), driverName()); + Timber.i("Connecting to [%s] (%sdriver: %s)", + device.getAddress(), autoConnect ? "auto connect, " : "", driverName()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { bluetoothGatt = device.connectGatt( - context, false, gattCallback, BluetoothDevice.TRANSPORT_LE); + context, autoConnect, gattCallback, BluetoothDevice.TRANSPORT_LE); } else { - bluetoothGatt = device.connectGatt(context, false, gattCallback); + bluetoothGatt = device.connectGatt(context, autoConnect, gattCallback); } } @@ -634,7 +636,7 @@ public abstract class BluetoothCommunication { */ protected class GattCallback extends BluetoothGattCallback { @Override - public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) { + public void onConnectionStateChange(final BluetoothGatt gatt, final int status, int newState) { Timber.d("onConnectionStateChange: status=%d, newState=%d", status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { @@ -668,6 +670,9 @@ public abstract class BluetoothCommunication { handler.post(new Runnable() { @Override public void run() { + if (connectionEstablished && status != 0) { + autoConnect = !autoConnect; + } setBtStatus(connectionEstablished ? BT_STATUS_CODE.BT_CONNECTION_LOST : BT_STATUS_CODE.BT_NO_DEVICE_FOUND); From d29884d0ff4bf65c2650876e3ba69c1059423cf5 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sat, 13 Oct 2018 19:21:17 +0200 Subject: [PATCH 09/14] Continue even if discoverServices return false To see if it helps #341 --- .../health/openscale/core/bluetooth/BluetoothCommunication.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index dc26de07..69326b3e 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -660,8 +660,6 @@ public abstract class BluetoothCommunication { public void run() { if (!gatt.discoverServices()) { Timber.e("Could not start service discovery"); - setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); - disconnect(false); } } }, 1000); From d5b5eb58d4976a35fae3676740240c0f6acc3e62 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 14 Oct 2018 22:16:37 +0200 Subject: [PATCH 10/14] Revert "Continue even if discoverServices return false" This reverts commit d29884d0ff4bf65c2650876e3ba69c1059423cf5. --- .../health/openscale/core/bluetooth/BluetoothCommunication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index 69326b3e..dc26de07 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -660,6 +660,8 @@ public abstract class BluetoothCommunication { public void run() { if (!gatt.discoverServices()) { Timber.e("Could not start service discovery"); + setBtStatus(BT_STATUS_CODE.BT_CONNECTION_LOST); + disconnect(false); } } }, 1000); From 9ef17a9c6a3cf5a32a80efa36b345fa184b3684d Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 14 Oct 2018 22:34:47 +0200 Subject: [PATCH 11/14] Let the LE scan run while connecting To see if it helps #341 --- .../bluetooth/BluetoothCommunication.java | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index dc26de07..f33030ff 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -405,16 +405,14 @@ public abstract class BluetoothCommunication { * @param hwAddress the Bluetooth address to connect to */ public void connect(String hwAddress) { - disconnect(false); - logBluetoothStatus(); + disconnect(false); + btAdapter.cancelDiscovery(); + // Some good tips to improve BLE connections: // https://android.jlelse.eu/lessons-for-first-time-android-bluetooth-le-developers-i-learned-the-hard-way-fee07646624 - btAdapter.cancelDiscovery(); - stopLeScan(); - // Don't do any cleanup if disconnected before fully connected btMachineState = BT_MACHINE_STATE.BT_CLEANUP_STATE; @@ -462,18 +460,6 @@ public abstract class BluetoothCommunication { } } - private void stopLeScanAndConnectGatt(final BluetoothDevice device) { - stopLeScan(); - - // Delay the call to connectGatt to let things settle a bit - handler.postDelayed(new Runnable() { - @Override - public void run() { - connectGatt(device); - } - }, 500); - } - private void startLeScanForDevice(final String hwAddress) { leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override @@ -486,23 +472,23 @@ public abstract class BluetoothCommunication { handler.post(new Runnable() { @Override public void run() { - // Check that callback != null in case the same device is found multiple times + // Check that bluetoothGatt == null in case the same device is found multiple times // and thus multiple calls to connect to it are queued. Only the first will - // have callback != null. - if (leScanCallback != null) { - stopLeScanAndConnectGatt(device); + // trigger the connect. + if (bluetoothGatt == null) { + connectGatt(device); } } }); } }; - // Stop scan and try to connect to the device directly if the device isn't found in time + // Try to connect to the device directly if the device isn't found in time handler.postAtTime(new Runnable() { @Override public void run() { Timber.d("Device not found in LE scan, connecting directly"); - stopLeScanAndConnectGatt(btAdapter.getRemoteDevice(hwAddress)); + connectGatt(btAdapter.getRemoteDevice(hwAddress)); } }, leScanCallback, SystemClock.uptimeMillis() + LE_SCAN_TIMEOUT_MS); @@ -533,6 +519,9 @@ public abstract class BluetoothCommunication { Timber.i("Disconnecting%s", doCleanup ? " (with cleanup)" : ""); + handler.removeCallbacksAndMessages(null); + callbackBtHandler = null; + if (doCleanup) { if (btMachineState != BT_MACHINE_STATE.BT_CLEANUP_STATE) { setBtMachineState(BT_MACHINE_STATE.BT_CLEANUP_STATE); @@ -544,9 +533,7 @@ public abstract class BluetoothCommunication { if (openRequest) { handler.postDelayed(this, 10); } else { - bluetoothGatt.close(); - bluetoothGatt = null; - handler.removeCallbacksAndMessages(null); + disconnect(false); } } }); @@ -555,9 +542,6 @@ public abstract class BluetoothCommunication { bluetoothGatt.close(); bluetoothGatt = null; } - - handler.removeCallbacksAndMessages(null); - callbackBtHandler = null; } /** From 23491ee60c6febdde229924b0b05f15082f68c15 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Sun, 14 Oct 2018 22:37:55 +0200 Subject: [PATCH 12/14] Remove call to readPhy added in 1929a7546 --- .../openscale/core/bluetooth/BluetoothCommunication.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index f33030ff..480f6b19 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -629,10 +629,6 @@ public abstract class BluetoothCommunication { public void run() { stopLeScan(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - gatt.readPhy(); - } - connectionEstablished = true; setBtStatus(BT_STATUS_CODE.BT_CONNECTION_ESTABLISHED); } From e97b1765299b87d83e39801ac3cb261a79f7a3f5 Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Mon, 15 Oct 2018 21:57:17 +0200 Subject: [PATCH 13/14] Stop timeout handler before connecting to scale --- .../openscale/core/bluetooth/BluetoothCommunication.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java index 480f6b19..218d660c 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothCommunication.java @@ -468,7 +468,8 @@ public abstract class BluetoothCommunication { if (!device.getAddress().equals(hwAddress)) { return; } - // Stop scan and connect to the device on the main thread + // Stop timeout and connect to the device on the main thread + handler.removeCallbacksAndMessages(leScanCallback); handler.post(new Runnable() { @Override public void run() { From 4ace7319e04d32cbaf967a06cce09ad2a84861cd Mon Sep 17 00:00:00 2001 From: Erik Johansson Date: Mon, 15 Oct 2018 22:02:01 +0200 Subject: [PATCH 14/14] Log name of selected user --- .../app/src/main/java/com/health/openscale/core/OpenScale.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java b/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java index 619d238b..9d51598a 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java +++ b/android_app/app/src/main/java/com/health/openscale/core/OpenScale.java @@ -195,6 +195,8 @@ public class OpenScale { selectScaleUser(-1); throw new Exception("could not find the selected user"); } + Timber.d("Selected user is now %s (%d)", + selectedScaleUser.getUserName(), selectedScaleUser.getId()); return selectedScaleUser; } } catch (Exception e) {