Skip to content

Commit ce6752e

Browse files
authored
Merge pull request #633 from NordicSemiconductor/feature/unsupported-configuration
Feature: unsupported configuration
2 parents abc0910 + 2c5b148 commit ce6752e

File tree

4 files changed

+63
-28
lines changed

4 files changed

+63
-28
lines changed

ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ abstract class BleManagerHandler extends RequestHandler {
180180
* Current connection parameters. Those values are only available starting from Android Oreo.
181181
*/
182182
private int interval, latency, timeout;
183+
/**
184+
* Samsung S8 with Android 9 fails to reconnect to devices requesting PHY LE 2M just after
185+
* connection. Workaround would be to disable PHY LE 2M on the device side.
186+
*/
187+
private boolean earlyPhyLe2MRequest;
183188
/**
184189
* Last received battery value or -1 if value wasn't received.
185190
*
@@ -753,6 +758,7 @@ private boolean internalConnect(@NonNull final BluetoothDevice device,
753758
postConnectionStateChange(o -> o.onDeviceConnecting(device));
754759
}
755760
connectionTime = SystemClock.elapsedRealtime();
761+
earlyPhyLe2MRequest = false;
756762
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
757763
// connectRequest will never be null here.
758764
final int preferredPhy = connectRequest.getPreferredPhy();
@@ -2301,7 +2307,10 @@ public void onConnectionStateChange(@NonNull final BluetoothGatt gatt,
23012307
// ...because the next method sets them to false.
23022308

23032309
// notifyDeviceDisconnected(...) may call close()
2304-
if (timeout) {
2310+
2311+
if (status == GattError.GATT_CONN_TIMEOUT && earlyPhyLe2MRequest) {
2312+
notifyDeviceDisconnected(gatt.getDevice(), ConnectionObserver.REASON_UNSUPPORTED_CONFIGURATION);
2313+
} else if (timeout) {
23052314
notifyDeviceDisconnected(gatt.getDevice(), ConnectionObserver.REASON_TIMEOUT);
23062315
} else if (notSupported) {
23072316
notifyDeviceDisconnected(gatt.getDevice(), ConnectionObserver.REASON_NOT_SUPPORTED);
@@ -2332,7 +2341,9 @@ public void onConnectionStateChange(@NonNull final BluetoothGatt gatt,
23322341
}
23332342
if (cr != null) {
23342343
int reason;
2335-
if (notSupported)
2344+
if (status == GattError.GATT_CONN_TIMEOUT && earlyPhyLe2MRequest)
2345+
reason = FailCallback.REASON_UNSUPPORTED_CONFIGURATION;
2346+
else if (notSupported)
23362347
reason = FailCallback.REASON_DEVICE_NOT_SUPPORTED;
23372348
else if (status == BluetoothGatt.GATT_SUCCESS)
23382349
reason = FailCallback.REASON_DEVICE_DISCONNECTED;
@@ -2524,13 +2535,9 @@ public void onCharacteristicRead(@NonNull final BluetoothGatt gatt,
25242535
rr.notifySuccess(gatt.getDevice());
25252536
}
25262537
}
2527-
} else if (status == 137 /* GATT AUTH FAIL */) {
2528-
// Bonding failed or was cancelled.
2529-
Log.w(TAG, "Reading failed with status " + status);
2530-
// The bond state receiver will fail the request. Stop here.
2531-
return;
25322538
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
2533-
|| status == 8 /* GATT INSUF AUTHORIZATION */) {
2539+
|| status == 8 /* GATT INSUF AUTHORIZATION */
2540+
|| status == 137 /* GATT AUTH FAIL */) {
25342541
// This is called when bonding attempt failed, but the app is still trying to read.
25352542
// We need to cancel the request here, as bonding won't start.
25362543
log(Log.WARN, () -> "Authentication required (" + status + ")");
@@ -2578,13 +2585,9 @@ public void onCharacteristicWrite(final BluetoothGatt gatt,
25782585
wr.notifySuccess(gatt.getDevice());
25792586
}
25802587
}
2581-
} else if (status == 137 /* GATT AUTH FAIL */) {
2582-
// This never happens for Write operations, for some reason.
2583-
Log.w(TAG, "Writing failed with status " + status);
2584-
// The bond state receiver will fail the request. Stop here.
2585-
return;
25862588
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
2587-
|| status == 8 /* GATT INSUF AUTHORIZATION */) {
2589+
|| status == 8 /* GATT INSUF AUTHORIZATION */
2590+
|| status == 137 /* GATT AUTH FAIL */) {
25882591
// This is called when bonding attempt failed, but the app is still trying to write.
25892592
// We need to cancel the request here, as bonding won't start.
25902593
log(Log.WARN, () -> "Authentication required (" + status + ")");
@@ -2595,6 +2598,9 @@ public void onCharacteristicWrite(final BluetoothGatt gatt,
25952598
}
25962599
if (request instanceof final WriteRequest wr) {
25972600
wr.notifyFail(gatt.getDevice(), status);
2601+
// Automatically abort Reliable Write when write error happen
2602+
if (requestQueue instanceof final ReliableWriteRequest rwr)
2603+
rwr.notifyAndCancelQueue(gatt.getDevice());
25982604
}
25992605
} else {
26002606
Log.e(TAG, "onCharacteristicWrite error " + status + ", bond state: " + gatt.getDevice().getBondState());
@@ -2658,13 +2664,9 @@ public void onDescriptorRead(final @NonNull BluetoothGatt gatt,
26582664
rr.notifySuccess(gatt.getDevice());
26592665
}
26602666
}
2661-
} else if (status == 137 /* GATT AUTH FAIL */) {
2662-
// Bonding failed or was cancelled.
2663-
Log.w(TAG, "Reading descriptor failed with status " + status);
2664-
// The bond state receiver will fail the request. Stop here.
2665-
return;
26662667
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
2667-
|| status == 8 /* GATT INSUF AUTHORIZATION */) {
2668+
|| status == 8 /* GATT INSUF AUTHORIZATION */
2669+
|| status == 137 /* GATT AUTH FAIL */) {
26682670
// This is called when bonding attempt failed, but the app is still trying to read.
26692671
// We need to cancel the request here, as bonding won't start.
26702672
log(Log.WARN, () -> "Authentication required (" + status + ")");
@@ -2722,13 +2724,9 @@ public void onDescriptorWrite(final BluetoothGatt gatt,
27222724
wr.notifySuccess(gatt.getDevice());
27232725
}
27242726
}
2725-
} else if (status == 137 /* GATT AUTH FAIL */) {
2726-
// This never happens for Write operations, for some reason.
2727-
Log.w(TAG, "Writing descriptor failed with status " + status);
2728-
// The bond state receiver will fail the request. Stop here.
2729-
return;
27302727
} else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
2731-
|| status == 8 /* GATT INSUF AUTHORIZATION */) {
2728+
|| status == 8 /* GATT INSUF AUTHORIZATION */
2729+
|| status == 137 /* GATT AUTH FAIL */) {
27322730
// This is called when bonding attempt failed, but the app is still trying to write.
27332731
// We need to cancel the request here, as bonding won't start.
27342732
log(Log.WARN, () -> "Authentication required (" + status + ")");
@@ -2739,6 +2737,9 @@ public void onDescriptorWrite(final BluetoothGatt gatt,
27392737
}
27402738
if (request instanceof final WriteRequest wr) {
27412739
wr.notifyFail(gatt.getDevice(), status);
2740+
// Automatically abort Reliable Write when write error happen
2741+
if (requestQueue instanceof final ReliableWriteRequest rwr)
2742+
rwr.notifyAndCancelQueue(gatt.getDevice());
27422743
}
27432744
} else {
27442745
Log.e(TAG, "onDescriptorWrite error " + status + ", bond state: " + gatt.getDevice().getBondState());
@@ -2958,6 +2959,9 @@ public void onPhyUpdate(@NonNull final BluetoothGatt gatt,
29582959
log(Log.INFO, () ->
29592960
"PHY updated (TX: " + ParserUtils.phyToString(txPhy) +
29602961
", RX: " + ParserUtils.phyToString(rxPhy) + ")");
2962+
// Samsung S8 fails to reconnect when PHY LE 2M request is sent before service discovery.
2963+
earlyPhyLe2MRequest = earlyPhyLe2MRequest ||
2964+
(txPhy == BluetoothDevice.PHY_LE_2M && !servicesDiscovered);
29612965
if (request instanceof final PhyRequest pr) {
29622966
pr.notifyPhyChanged(gatt.getDevice(), txPhy, rxPhy);
29632967
pr.notifySuccess(gatt.getDevice());

ble/src/main/java/no/nordicsemi/android/ble/callback/FailCallback.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,37 @@
2323
package no.nordicsemi.android.ble.callback;
2424

2525
import android.bluetooth.BluetoothDevice;
26+
import android.bluetooth.BluetoothGatt;
27+
import no.nordicsemi.android.ble.BleManager;
2628

2729
import androidx.annotation.NonNull;
2830

2931
@FunctionalInterface
3032
public interface FailCallback {
3133
int REASON_DEVICE_DISCONNECTED = -1;
34+
/**
35+
* Returned when the {@link BleManager#isRequiredServiceSupported(BluetoothGatt)}
36+
* returns false, that is when at least required GATT service was not discovered
37+
* on the connected device.
38+
*/
3239
int REASON_DEVICE_NOT_SUPPORTED = -2;
3340
int REASON_NULL_ATTRIBUTE = -3;
3441
int REASON_REQUEST_FAILED = -4;
3542
int REASON_TIMEOUT = -5;
3643
int REASON_VALIDATION = -6;
3744
int REASON_CANCELLED = -7;
3845
int REASON_NOT_ENABLED = -8;
46+
/**
47+
* The Android device is unable to reconnect to the peripheral because of internal failure.
48+
* Most probably it cannot respond properly to PHY LE 2M update procedure, causing the
49+
* remote device to terminate the connection.
50+
* <p>
51+
* Try disabling PHY LE 2M on the peripheral side, or update the Android version.
52+
* If that's not possible, the connection to your device may not work on the given
53+
* Android device at all. If the device is bonded, try removing bonding and connect,
54+
* but this seems to fix the problem only before a new bond is created.
55+
*/
56+
int REASON_UNSUPPORTED_CONFIGURATION = -9;
3957
int REASON_BLUETOOTH_DISABLED = -100;
4058

4159
/**
@@ -47,7 +65,8 @@ public interface FailCallback {
4765
* {@link #REASON_DEVICE_DISCONNECTED}, {@link #REASON_TIMEOUT},
4866
* {@link #REASON_DEVICE_NOT_SUPPORTED} (only for Connect request),
4967
* {@link #REASON_BLUETOOTH_DISABLED}, {@link #REASON_NULL_ATTRIBUTE},
50-
* {@link #REASON_VALIDATION}, {@link #REASON_CANCELLED}, {@link #REASON_NOT_ENABLED}
68+
* {@link #REASON_VALIDATION}, {@link #REASON_CANCELLED}, {@link #REASON_NOT_ENABLED},
69+
* {@link #REASON_UNSUPPORTED_CONFIGURATION},
5170
* or {@link #REASON_REQUEST_FAILED} (for other reason).
5271
*/
5372
void onRequestFailed(@NonNull final BluetoothDevice device, final int status);

ble/src/main/java/no/nordicsemi/android/ble/error/GattError.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ public static String parseConnectionError(final int error) {
104104
return switch (error) {
105105
case GATT_SUCCESS -> "SUCCESS";
106106
case GATT_CONN_L2C_FAILURE -> "GATT CONN L2C FAILURE";
107+
case GATT_INSUF_AUTHENTICATION -> "GATT INSUF AUTHENTICATION";
107108
case GATT_CONN_TIMEOUT -> "GATT CONN TIMEOUT";
108109
case GATT_CONN_TERMINATE_PEER_USER -> "GATT CONN TERMINATE PEER USER";
109110
case GATT_CONN_TERMINATE_LOCAL_HOST -> "GATT CONN TERMINATE LOCAL HOST";

ble/src/main/java/no/nordicsemi/android/ble/observer/ConnectionObserver.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public interface ConnectionObserver {
4949
* Android will try to reconnect automatically.
5050
*/
5151
int REASON_LINK_LOSS = 3;
52-
/** The device does not hav required services. */
52+
/** The device does not have required services. */
5353
int REASON_NOT_SUPPORTED = 4;
5454
/** Connection attempt was cancelled. */
5555
int REASON_CANCELLED = 5;
@@ -58,6 +58,17 @@ public interface ConnectionObserver {
5858
* or doesn't respond for another reason.
5959
*/
6060
int REASON_TIMEOUT = 10;
61+
/**
62+
* The Android device is unable to reconnect to the peripheral because of internal failure.
63+
* Most probably it cannot respond properly to PHY LE 2M update procedure, causing the
64+
* remote device to terminate the connection.
65+
* <p>
66+
* Try disabling PHY LE 2M on the peripheral side, or update the Android version.
67+
* If that's not possible, the connection to your device may not work on the given
68+
* Android device at all. If the device is bonded, try removing bonding and connect,
69+
* but this seems to fix the problem only before a new bond is created.
70+
*/
71+
int REASON_UNSUPPORTED_CONFIGURATION = 11;
6172

6273
/**
6374
* Called when the Android device started connecting to given device.

0 commit comments

Comments
 (0)