[bluetooth.generic] Enable BLE notification for linked channels (#10122)

* [bluetooth] Add BluetoothDevice.isNotifying()
* [bluetooth] Improve Characteristic properties support
* [bluez] Improve Characteristic properties support
* [bluetooth] Add BluetoothDevice.canNotify()
* [bluez] Also catch DBusExecutionException on read value
* [bluetooth.generic] Activate notifications for linked channels where characteristics are able to notify
* [bluez] Adjust javadoc
* [bluegiga] Add BluetoothDevice.isNotifying() support
* [bluegiga] Fix notification enabled check
* [bluetooth] move canNotify() to Characteristic
* [bluegiga] rename notificationEnabled to notifying
* [bluetooth.generic] use handlerToChannels to subscribe to notifications
* [bluetooth.generic] implement TODOs of canRead()/canWrite()
* [bluetooth.generic] optimize ChannelUID
* [bluetooth.generic] use channelUids for link check

Signed-off-by: Peter Rosenberg <prosenb.dev@gmail.com>
This commit is contained in:
Pete
2021-02-16 21:26:34 +01:00
committed by GitHub
parent 596b261d47
commit 7abeb97396
9 changed files with 270 additions and 22 deletions

View File

@@ -58,6 +58,7 @@ import org.sputnikdev.bluetooth.gattparser.spec.Field;
* channels based off of a bluetooth device's GATT characteristics.
*
* @author Connor Petty - Initial contribution
* @author Peter Rosenberg - Use notifications
*
*/
@NonNullByDefault
@@ -68,6 +69,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
private final Map<ChannelUID, CharacteristicHandler> channelHandlers = new ConcurrentHashMap<>();
private final BluetoothGattParser gattParser = BluetoothGattParserFactory.getDefault();
private final CharacteristicChannelTypeProvider channelTypeProvider;
private final Map<CharacteristicHandler, List<ChannelUID>> handlerToChannels = new ConcurrentHashMap<>();
private @Nullable ScheduledFuture<?> readCharacteristicJob = null;
@@ -84,12 +86,14 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
readCharacteristicJob = scheduler.scheduleWithFixedDelay(() -> {
if (device.getConnectionState() == ConnectionState.CONNECTED) {
if (resolved) {
for (CharacteristicHandler charHandler : charHandlers.values()) {
if (charHandler.canRead()) {
handlerToChannels.forEach((charHandler, channelUids) -> {
// Only read the value manually if notification is not on.
// Also read it the first time before we activate notifications below.
if (!device.isNotifying(charHandler.characteristic) && charHandler.canRead()) {
device.readCharacteristic(charHandler.characteristic);
try {
// TODO the ideal solution would be to use locks/conditions and timeouts
// between this code and `onCharacteristicReadComplete` but
// Kbetween this code and `onCharacteristicReadComplete` but
// that would overcomplicate the code a bit and I plan
// on implementing a better more generalized solution later
Thread.sleep(50);
@@ -97,7 +101,20 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
return;
}
}
}
if (charHandler.characteristic.canNotify()) {
// Enabled/Disable notifications dependent on if the channel is linked.
// TODO check why isLinked() is true for not linked channels
if (channelUids.stream().anyMatch(this::isLinked)) {
if (!device.isNotifying(charHandler.characteristic)) {
device.enableNotifications(charHandler.characteristic);
}
} else {
if (device.isNotifying(charHandler.characteristic)) {
device.disableNotifications(charHandler.characteristic);
}
}
}
});
} else {
// if we are connected and still haven't been able to resolve the services, try disconnecting and
// then connecting again
@@ -117,6 +134,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
charHandlers.clear();
channelHandlers.clear();
handlerToChannels.clear();
}
@Override
@@ -161,9 +179,11 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
logger.trace("{} processing characteristic {}", address, characteristic.getUuid());
CharacteristicHandler handler = getCharacteristicHandler(characteristic);
List<Channel> chans = handler.buildChannels();
for (Channel channel : chans) {
channelHandlers.put(channel.getUID(), handler);
List<ChannelUID> chanUids = chans.stream().map(Channel::getUID).collect(Collectors.toList());
for (ChannelUID channel : chanUids) {
channelHandlers.put(channel, handler);
}
handlerToChannels.put(handler, chanUids);
return chans.stream();
})//
.collect(Collectors.toList());
@@ -341,8 +361,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
if (gattParser.isKnownCharacteristic(charUUID)) {
return gattParser.isValidForRead(charUUID);
}
// TODO: need to evaluate this from characteristic properties, but such properties aren't support yet
return true;
return characteristic.canRead();
}
public boolean canWrite() {
@@ -350,8 +369,7 @@ public class GenericBluetoothHandler extends ConnectedBluetoothHandler {
if (gattParser.isKnownCharacteristic(charUUID)) {
return gattParser.isValidForWrite(charUUID);
}
// TODO: need to evaluate this from characteristic properties, but such properties aren't support yet
return true;
return characteristic.canWrite();
}
private boolean isAdvanced() {