[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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 270 additions and 22 deletions

View File

@ -27,7 +27,7 @@ import org.openhab.binding.bluetooth.BluetoothCharacteristic;
*/ */
public class BlueGigaBluetoothCharacteristic extends BluetoothCharacteristic { public class BlueGigaBluetoothCharacteristic extends BluetoothCharacteristic {
private boolean notificationEnabled; private boolean notifying;
public BlueGigaBluetoothCharacteristic(int handle) { public BlueGigaBluetoothCharacteristic(int handle) {
super(null, handle); super(null, handle);
@ -45,11 +45,11 @@ public class BlueGigaBluetoothCharacteristic extends BluetoothCharacteristic {
this.uuid = uuid; this.uuid = uuid;
} }
public boolean isNotificationEnabled() { public boolean isNotifying() {
return notificationEnabled; return notifying;
} }
public void setNotificationEnabled(boolean enable) { public void setNotifying(boolean enable) {
this.notificationEnabled = enable; this.notifying = enable;
} }
} }

View File

@ -196,7 +196,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
} }
BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic; BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic;
if (ch.isNotificationEnabled()) { if (ch.isNotifying()) {
return true; return true;
} }
@ -241,12 +241,12 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
@Override @Override
public boolean disableNotifications(BluetoothCharacteristic characteristic) { public boolean disableNotifications(BluetoothCharacteristic characteristic) {
if (connection == -1) { if (connection == -1) {
logger.debug("Cannot enable notifications, device not connected {}", this); logger.debug("Cannot disable notifications, device not connected {}", this);
return false; return false;
} }
BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic; BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic;
if (ch.isNotificationEnabled()) { if (!ch.isNotifying()) {
return true; return true;
} }
@ -288,6 +288,12 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
return true; return true;
} }
@Override
public boolean isNotifying(BluetoothCharacteristic characteristic) {
BlueGigaBluetoothCharacteristic ch = (BlueGigaBluetoothCharacteristic) characteristic;
return ch.isNotifying();
}
@Override @Override
public boolean enableNotifications(BluetoothDescriptor descriptor) { public boolean enableNotifications(BluetoothDescriptor descriptor) {
// TODO will be implemented in a followup PR // TODO will be implemented in a followup PR
@ -613,7 +619,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
if (!success) { if (!success) {
logger.debug("write to descriptor failed"); logger.debug("write to descriptor failed");
} }
((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotificationEnabled(success); ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotifying(success);
procedureProgress = BlueGigaProcedure.NONE; procedureProgress = BlueGigaProcedure.NONE;
procedureCharacteristic = null; procedureCharacteristic = null;
break; break;
@ -622,7 +628,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
if (!success) { if (!success) {
logger.debug("write to descriptor failed"); logger.debug("write to descriptor failed");
} }
((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotificationEnabled(!success); ((BlueGigaBluetoothCharacteristic) procedureCharacteristic).setNotifying(!success);
procedureProgress = BlueGigaProcedure.NONE; procedureProgress = BlueGigaProcedure.NONE;
procedureCharacteristic = null; procedureCharacteristic = null;
break; break;
@ -656,7 +662,7 @@ public class BlueGigaBluetoothDevice extends BaseBluetoothDevice implements Blue
} }
for (BlueGigaBluetoothCharacteristic ch : handleToCharacteristic.values()) { for (BlueGigaBluetoothCharacteristic ch : handleToCharacteristic.values()) {
ch.setNotificationEnabled(false); ch.setNotifying(false);
} }
cancelTimer(procedureTimer); cancelTimer(procedureTimer);

View File

@ -57,6 +57,7 @@ import com.github.hypfvieh.bluetooth.wrapper.BluetoothGattService;
* *
* @author Kai Kreuzer - Initial contribution and API * @author Kai Kreuzer - Initial contribution and API
* @author Benjamin Lafois - Replaced tinyB with bluezDbus * @author Benjamin Lafois - Replaced tinyB with bluezDbus
* @author Peter Rosenberg - Improve notifications and properties support
* *
*/ */
@NonNullByDefault @NonNullByDefault
@ -399,6 +400,7 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
for (BluetoothGattCharacteristic dBusBlueZCharacteristic : dBusBlueZService.getGattCharacteristics()) { for (BluetoothGattCharacteristic dBusBlueZCharacteristic : dBusBlueZService.getGattCharacteristics()) {
BluetoothCharacteristic characteristic = new BluetoothCharacteristic( BluetoothCharacteristic characteristic = new BluetoothCharacteristic(
UUID.fromString(dBusBlueZCharacteristic.getUuid()), 0); UUID.fromString(dBusBlueZCharacteristic.getUuid()), 0);
convertCharacteristicProperties(dBusBlueZCharacteristic, characteristic);
for (BluetoothGattDescriptor dBusBlueZDescriptor : dBusBlueZCharacteristic.getGattDescriptors()) { for (BluetoothGattDescriptor dBusBlueZDescriptor : dBusBlueZCharacteristic.getGattDescriptors()) {
BluetoothDescriptor descriptor = new BluetoothDescriptor(characteristic, BluetoothDescriptor descriptor = new BluetoothDescriptor(characteristic,
@ -414,6 +416,42 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
return true; return true;
} }
/**
* Convert the flags of BluetoothGattCharacteristic to the int bitset used by BluetoothCharacteristic.
*
* @param dBusBlueZCharacteristic source characteristic to read the flags from
* @param characteristic destination characteristic to write to properties to
*/
private void convertCharacteristicProperties(BluetoothGattCharacteristic dBusBlueZCharacteristic,
BluetoothCharacteristic characteristic) {
int properties = 0;
for (String property : dBusBlueZCharacteristic.getFlags()) {
switch (property) {
case "broadcast":
properties |= BluetoothCharacteristic.PROPERTY_BROADCAST;
break;
case "read":
properties |= BluetoothCharacteristic.PROPERTY_READ;
break;
case "write-without-response":
properties |= BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
break;
case "write":
properties |= BluetoothCharacteristic.PROPERTY_WRITE;
break;
case "notify":
properties |= BluetoothCharacteristic.PROPERTY_NOTIFY;
break;
case "indicate":
properties |= BluetoothCharacteristic.PROPERTY_INDICATE;
break;
}
}
characteristic.setProperties(properties);
}
@Override @Override
public boolean readCharacteristic(BluetoothCharacteristic characteristic) { public boolean readCharacteristic(BluetoothCharacteristic characteristic) {
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString()); BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
@ -428,7 +466,8 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
characteristic.setValue(value); characteristic.setValue(value);
notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic,
BluetoothCompletionStatus.SUCCESS); BluetoothCompletionStatus.SUCCESS);
} catch (DBusException e) { } catch (DBusException | DBusExecutionException e) {
// DBusExecutionException is thrown if the value cannot be read
logger.debug("Exception occurred when trying to read characteristic '{}': {}", characteristic.getUuid(), logger.debug("Exception occurred when trying to read characteristic '{}': {}", characteristic.getUuid(),
e.getMessage()); e.getMessage());
notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic, notifyListeners(BluetoothEventType.CHARACTERISTIC_READ_COMPLETE, characteristic,
@ -438,6 +477,18 @@ public class BlueZBluetoothDevice extends BaseBluetoothDevice implements BlueZEv
return true; return true;
} }
@Override
public boolean isNotifying(BluetoothCharacteristic characteristic) {
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());
if (c != null) {
Boolean isNotifying = c.isNotifying();
return Objects.requireNonNullElse(isNotifying, false);
} else {
logger.warn("Characteristic '{}' is missing on device '{}'.", characteristic.getUuid(), address);
return false;
}
}
@Override @Override
public boolean disableNotifications(BluetoothCharacteristic characteristic) { public boolean disableNotifications(BluetoothCharacteristic characteristic) {
BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString()); BluetoothGattCharacteristic c = getDBusBlueZCharacteristicByUUID(characteristic.getUuid().toString());

View File

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

View File

@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory;
* *
* @author Chris Jackson - Initial contribution * @author Chris Jackson - Initial contribution
* @author Kai Kreuzer - Cleaned up code * @author Kai Kreuzer - Cleaned up code
* @author Peter Rosenberg - Improve properties support
*/ */
public class BluetoothCharacteristic { public class BluetoothCharacteristic {
public static final int FORMAT_UINT8 = 0x11; public static final int FORMAT_UINT8 = 0x11;
@ -142,6 +143,16 @@ public class BluetoothCharacteristic {
return instance; return instance;
} }
/**
* Set the raw properties. The individual properties are represented as bits inside
* of this int value.
*
* @param properties of this Characteristic
*/
public void setProperties(int properties) {
this.properties = properties;
}
/** /**
* Returns the properties of this characteristic. * Returns the properties of this characteristic.
* *
@ -152,6 +163,46 @@ public class BluetoothCharacteristic {
return properties; return properties;
} }
/**
* Returns if the given characteristics property is enabled or not.
*
* @param property one of the Constants BluetoothCharacteristic.PROPERTY_XX
* @return true if this characteristic has the given property enabled, false if properties not set or
* the given property is not enabled.
*/
public boolean hasPropertyEnabled(int property) {
return (properties & property) != 0;
}
/**
* Returns if notifications can be enabled on this characteristic.
*
* @return true if notifications can be enabled, false if notifications are not supported, characteristic is missing
* on device or notifications are not supported.
*/
public boolean canNotify() {
return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY);
}
/**
* Returns if the value can be read on this characteristic.
*
* @return true if the value can be read, false otherwise.
*/
public boolean canRead() {
return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ);
}
/**
* Returns if the value can be written on this characteristic.
*
* @return true if the value can be written with of without a response, false otherwise.
*/
public boolean canWrite() {
return hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE)
|| hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
}
/** /**
* Returns the permissions for this characteristic. * Returns the permissions for this characteristic.
*/ */

View File

@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
* @author Chris Jackson - Initial contribution * @author Chris Jackson - Initial contribution
* @author Kai Kreuzer - Refactored class to use Integer instead of int, fixed bugs, diverse improvements * @author Kai Kreuzer - Refactored class to use Integer instead of int, fixed bugs, diverse improvements
* @author Connor Petty - Made most of the methods abstract * @author Connor Petty - Made most of the methods abstract
* @author Peter Rosenberg - Improve notifications
*/ */
@NonNullByDefault @NonNullByDefault
public abstract class BluetoothDevice { public abstract class BluetoothDevice {
@ -249,6 +250,15 @@ public abstract class BluetoothDevice {
*/ */
public abstract boolean writeCharacteristic(BluetoothCharacteristic characteristic); public abstract boolean writeCharacteristic(BluetoothCharacteristic characteristic);
/**
* Returns if notification is enabled for the given characteristic.
*
* @param characteristic the {@link BluetoothCharacteristic} to check if notifications are enabled.
* @return true if notification is enabled, false if notification is disabled, characteristic is missing on device
* or notifications are not supported.
*/
public abstract boolean isNotifying(BluetoothCharacteristic characteristic);
/** /**
* Enables notifications for a characteristic. Only a single read or write operation can be requested at once. * Enables notifications for a characteristic. Only a single read or write operation can be requested at once.
* Attempting to perform an operation when one is already in progress will result in subsequent calls returning * Attempting to perform an operation when one is already in progress will result in subsequent calls returning

View File

@ -112,6 +112,12 @@ public abstract class DelegateBluetoothDevice extends BluetoothDevice {
return delegate != null && delegate.writeCharacteristic(characteristic); return delegate != null && delegate.writeCharacteristic(characteristic);
} }
@Override
public boolean isNotifying(BluetoothCharacteristic characteristic) {
BluetoothDevice delegate = getDelegate();
return delegate != null ? delegate.isNotifying(characteristic) : false;
}
@Override @Override
public boolean enableNotifications(BluetoothCharacteristic characteristic) { public boolean enableNotifications(BluetoothCharacteristic characteristic) {
BluetoothDevice delegate = getDelegate(); BluetoothDevice delegate = getDelegate();

View File

@ -0,0 +1,101 @@
/**
* Copyright (c) 2010-2021 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.bluetooth;
import static org.junit.jupiter.api.Assertions.*;
import java.util.UUID;
import org.junit.jupiter.api.Test;
/**
* Tests {@link BluetoothCharacteristic}.
*
* @author Peter Rosenberg - Initial contribution
*/
public class CharacteristicPropertiesTest {
private BluetoothCharacteristic characteristic = new BluetoothCharacteristic(UUID.randomUUID(), 0);
@Test
public void testAllSupportedProperties() {
// given
// when
int properties = 0;
properties |= BluetoothCharacteristic.PROPERTY_BROADCAST;
properties |= BluetoothCharacteristic.PROPERTY_READ;
properties |= BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE;
properties |= BluetoothCharacteristic.PROPERTY_WRITE;
properties |= BluetoothCharacteristic.PROPERTY_NOTIFY;
properties |= BluetoothCharacteristic.PROPERTY_INDICATE;
characteristic.setProperties(properties);
// then
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST), "Broastcast not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE),
"Write not response not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE),
"Signed write set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS),
"Extended props set");
}
@Test
public void testNoProperties() {
// given
// when
int properties = 0;
characteristic.setProperties(properties);
// then
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST),
"Broastcast not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE),
"Write not response not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE),
"Signed write set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS),
"Extended props set");
}
@Test
public void testSomeSupportedProperties() {
// given
// when
int properties = 0;
properties |= BluetoothCharacteristic.PROPERTY_READ;
properties |= BluetoothCharacteristic.PROPERTY_NOTIFY;
characteristic.setProperties(properties);
// then
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_BROADCAST),
"Broastcast not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_READ), "Read not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE_NO_RESPONSE),
"Write not response not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_WRITE), "Write not set");
assertTrue(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_NOTIFY), "Notify not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_INDICATE), "Indicate not set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_SIGNED_WRITE),
"Signed write set");
assertFalse(characteristic.hasPropertyEnabled(BluetoothCharacteristic.PROPERTY_EXTENDED_PROPS),
"Extended props set");
}
}

View File

@ -102,6 +102,11 @@ public class MockBluetoothDevice extends BaseBluetoothDevice {
return false; return false;
} }
@Override
public boolean isNotifying(BluetoothCharacteristic characteristic) {
return false;
}
@Override @Override
public boolean disableNotifications(BluetoothCharacteristic characteristic) { public boolean disableNotifications(BluetoothCharacteristic characteristic) {
return false; return false;