From 168c6a8a9717a6d4a09e25c88d7c6eeb1d6e50ad Mon Sep 17 00:00:00 2001 From: Grega Podlesek Date: Thu, 29 Feb 2024 20:11:40 +0100 Subject: [PATCH 1/5] Add shouldAutoConnectCreateDirectConnectionFirst to ConnectionRequest.useAutoConnect() If set to false, the first connection is done with autoConnect parameter equal to `autoConnect` as passed in ConnectionRequest.useAutoConnect(). --- .../android/ble/BleManagerHandler.java | 35 +++++++++++-------- .../android/ble/ConnectRequest.java | 11 ++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java index b173a815..35549fcb 100644 --- a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java +++ b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java @@ -689,15 +689,22 @@ private boolean internalConnect(@NonNull final BluetoothDevice device, // when retrying to create a connection. if (connectRequest == null) return false; + final boolean shouldAutoConnect = connectRequest.shouldAutoConnect(); - // We will receive Link Loss events only when the device is connected with autoConnect=true. - userDisconnected = !shouldAutoConnect; - // The first connection will always be done with autoConnect = false to make the connection quick. - // If the shouldAutoConnect() method returned true, the manager will automatically try to - // reconnect to this device on link loss. + final boolean autoConnect; if (shouldAutoConnect) { - initialConnection = true; + // If shouldAutoConnectCreateDirectConnectionFirst() returns true, the first connection + // will always be done with autoConnect = false to make the connection quick. + // If the shouldAutoConnect() method returned true, the manager will automatically try + // to reconnect to this device on link loss. + initialConnection = connectRequest.shouldAutoConnectCreateDirectConnectionFirst(); + autoConnect = !initialConnection; + } else { + autoConnect = false; } + // We will receive Link Loss events only when the device is connected with autoConnect=true. + userDisconnected = !shouldAutoConnect; + bluetoothDevice = device; log(Log.VERBOSE, () -> connectRequest.isFirstAttempt() ? "Connecting..." : "Retrying..."); connectionState = BluetoothGatt.STATE_CONNECTING; @@ -708,29 +715,29 @@ private boolean internalConnect(@NonNull final BluetoothDevice device, // connectRequest will never be null here. final int preferredPhy = connectRequest.getPreferredPhy(); log(Log.DEBUG, () -> - "gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, " + "gatt = device.connectGatt(autoConnect = " + autoConnect + ", TRANSPORT_LE, " + ParserUtils.phyMaskToString(preferredPhy) + ")"); - bluetoothGatt = device.connectGatt(context, false, gattCallback, + bluetoothGatt = device.connectGatt(context, autoConnect, gattCallback, BluetoothDevice.TRANSPORT_LE, preferredPhy, handler); } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { // connectRequest will never be null here. final int preferredPhy = connectRequest.getPreferredPhy(); log(Log.DEBUG, () -> - "gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, " + "gatt = device.connectGatt(autoConnect = " + autoConnect + ", TRANSPORT_LE, " + ParserUtils.phyMaskToString(preferredPhy) + ")"); // A variant of connectGatt with Handled can't be used here. // Check https://github.com/NordicSemiconductor/Android-BLE-Library/issues/54 // This bug specifically occurs in SDK 26 and is fixed in SDK 27 - bluetoothGatt = device.connectGatt(context, false, gattCallback, + bluetoothGatt = device.connectGatt(context, autoConnect, gattCallback, BluetoothDevice.TRANSPORT_LE, preferredPhy/*, handler*/); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - log(Log.DEBUG, () -> "gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE)"); - bluetoothGatt = device.connectGatt(context, false, gattCallback, + log(Log.DEBUG, () -> "gatt = device.connectGatt(autoConnect = " + autoConnect + ", TRANSPORT_LE)"); + bluetoothGatt = device.connectGatt(context, autoConnect, gattCallback, BluetoothDevice.TRANSPORT_LE); } else { - log(Log.DEBUG, () -> "gatt = device.connectGatt(autoConnect = false)"); - bluetoothGatt = device.connectGatt(context, false, gattCallback); + log(Log.DEBUG, () -> "gatt = device.connectGatt(autoConnect = " + autoConnect + ")"); + bluetoothGatt = device.connectGatt(context, autoConnect, gattCallback); } return true; } diff --git a/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java b/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java index 9b11766f..fc117448 100644 --- a/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java +++ b/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java @@ -60,6 +60,7 @@ public class ConnectRequest extends TimeoutableRequest { @IntRange(from = 0) private int delay = 0; private boolean autoConnect = false; + private boolean autoConnectCreateDirectConnectionFirst = true; ConnectRequest(@NonNull final Type type, @NonNull final BluetoothDevice device) { super(type); @@ -209,6 +210,12 @@ public ConnectRequest useAutoConnect(final boolean autoConnect) { return this; } + public ConnectRequest useAutoConnect(final boolean autoConnect, final boolean createDirectConnectionFirst) { + this.autoConnect = autoConnect; + this.autoConnectCreateDirectConnectionFirst = createDirectConnectionFirst; + return this; + } + /** * Sets the preferred PHY used for connection. The value should be a bitmask composed of * {@link PhyRequest#PHY_LE_1M_MASK}, {@link PhyRequest#PHY_LE_2M_MASK} or @@ -293,4 +300,8 @@ int getRetryDelay() { boolean shouldAutoConnect() { return autoConnect; } + + boolean shouldAutoConnectCreateDirectConnectionFirst() { + return autoConnectCreateDirectConnectionFirst; + } } From 8e5e1d244bdc08545c552559b94e16bce93425dd Mon Sep 17 00:00:00 2001 From: Grega Podlesek Date: Mon, 4 Mar 2024 10:55:48 +0100 Subject: [PATCH 2/5] To be consistent, don't report "Connecting" state when connecting with autoConect == true --- .../no/nordicsemi/android/ble/BleManagerHandler.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java index 35549fcb..cfb4b008 100644 --- a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java +++ b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java @@ -706,10 +706,12 @@ private boolean internalConnect(@NonNull final BluetoothDevice device, userDisconnected = !shouldAutoConnect; bluetoothDevice = device; - log(Log.VERBOSE, () -> connectRequest.isFirstAttempt() ? "Connecting..." : "Retrying..."); - connectionState = BluetoothGatt.STATE_CONNECTING; - postCallback(c -> c.onDeviceConnecting(device)); - postConnectionStateChange(o -> o.onDeviceConnecting(device)); + if (!autoConnect) { + log(Log.VERBOSE, () -> connectRequest.isFirstAttempt() ? "Connecting..." : "Retrying..."); + connectionState = BluetoothGatt.STATE_CONNECTING; + postCallback(c -> c.onDeviceConnecting(device)); + postConnectionStateChange(o -> o.onDeviceConnecting(device)); + } connectionTime = SystemClock.elapsedRealtime(); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { // connectRequest will never be null here. From 41364abf40fd55a23b8cd0f62426be2dc8db6f77 Mon Sep 17 00:00:00 2001 From: Grega Podlesek Date: Tue, 5 Mar 2024 13:05:51 +0100 Subject: [PATCH 3/5] Call success on ConnectRequest right after connectGatt if connecting with autoConnect = true. --- .../java/no/nordicsemi/android/ble/BleManagerHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java index cfb4b008..a6645531 100644 --- a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java +++ b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java @@ -741,6 +741,10 @@ private boolean internalConnect(@NonNull final BluetoothDevice device, log(Log.DEBUG, () -> "gatt = device.connectGatt(autoConnect = " + autoConnect + ")"); bluetoothGatt = device.connectGatt(context, autoConnect, gattCallback); } + + if (autoConnect && this.connectRequest != null) { + this.connectRequest.notifySuccess(device); + } return true; } From 78092f8c43df05316efd2cd4f007607e4fa7dff9 Mon Sep 17 00:00:00 2001 From: Grega Podlesek Date: Thu, 14 Mar 2024 21:16:13 +0100 Subject: [PATCH 4/5] Set this.connectRequest to null after notifying success. --- .../main/java/no/nordicsemi/android/ble/BleManagerHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java index a6645531..d3a9edda 100644 --- a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java +++ b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java @@ -744,6 +744,7 @@ private boolean internalConnect(@NonNull final BluetoothDevice device, if (autoConnect && this.connectRequest != null) { this.connectRequest.notifySuccess(device); + this.connectRequest = null; } return true; } From 85459c7afcfa84074dc6b2bd17ce9cbd26b59135 Mon Sep 17 00:00:00 2001 From: Grega Podlesek Date: Thu, 14 Mar 2024 21:16:19 +0100 Subject: [PATCH 5/5] Add documentation to the new useAutoConnect method. --- .../android/ble/ConnectRequest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java b/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java index fc117448..cac63917 100644 --- a/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java +++ b/ble/src/main/java/no/nordicsemi/android/ble/ConnectRequest.java @@ -210,6 +210,41 @@ public ConnectRequest useAutoConnect(final boolean autoConnect) { return this; } + /** + * Sets whether to connect to the remote device just once (autoConnect == false) or to add + * the address to white list of devices that will be automatically connect as soon as they + * become available (autoConnect == true). In the latter case, if Bluetooth adapter is enabled, + * Android scans periodically for devices from the white list and, if an advertising packet + * is received from such, it tries to connect to it. + * When the connection is lost, the system will keep trying to reconnect to + * it. If method is called with autoConnect set to true, and the connection to the device is + * lost, the {@link BleManagerCallbacks#onLinkLossOccurred(BluetoothDevice)} callback is + * called instead of {@link BleManagerCallbacks#onDeviceDisconnected(BluetoothDevice)}. + *

+ * This feature works much better on newer Android phone models and may have issues on older + * phones. + *

+ * This method should only be used with bonded devices, as otherwise the device may change + * it's address. It will however work also with non-bonded devices with private static address. + * A connection attempt to a non-bonded device with private resolvable address will fail. + *

+ * If createDirectConnectionFirst is set to true, the first connection to a device will always be + * created with autoConnect flag to false + * (see {@link BluetoothDevice#connectGatt(Context, boolean, BluetoothGattCallback)}). This is + * to make it quick as the user most probably waits for a quick response. If autoConnect is + * used (true), the following connections will be done using {@link BluetoothGatt#connect()}, + * which forces the autoConnect parameter to true. + * If autoConnect is used (true) and createDirectConnectionFirst is set to false, the connection + * to a device will be created with autoConnect flag to true from the start. + * + * @param autoConnect true to use autoConnect feature. + * @param createDirectConnectionFirst If true, the first connection is always done with autoConnect + * parameter equal to false, to make it faster and allow to timeout + * if the device is unreachable. + * If false, the connection to a device will be created with + * autoConnect flag to true from the start. + * @return The request. + */ public ConnectRequest useAutoConnect(final boolean autoConnect, final boolean createDirectConnectionFirst) { this.autoConnect = autoConnect; this.autoConnectCreateDirectConnectionFirst = createDirectConnectionFirst;