public abstract class BleManager extends java.lang.Object implements ILogger
The BleManager is responsible for managing the low level communication with a Bluetooth LE device. Please see profiles implementation in Android nRF Blinky or Android nRF Toolbox app for an example of use.
This base manager has been tested against number of devices and samples from Nordic SDK.
The manager handles connection events and initializes the device after establishing the connection.
BleManagerHandler.initialize() method for more details.ConnectRequest.done(SuccessCallback)
callback is called.
The manager also is responsible for parsing the Battery Level values and calling
BleManagerCallbacks.onBatteryValueReceived(BluetoothDevice, int) method.
To get logs, override the log(int, String) method.
The BleManager should be overridden in your app and all the 'high level' callbacks should be called from there.
| Modifier and Type | Class and Description |
|---|---|
protected static class |
BleManager.BleManagerGattCallback
The GATT Callback handler.
|
| Modifier and Type | Field and Description |
|---|---|
protected BleManagerCallbacks |
callbacks
Deprecated.
|
static int |
PAIRING_VARIANT_CONSENT |
static int |
PAIRING_VARIANT_DISPLAY_PASSKEY |
static int |
PAIRING_VARIANT_DISPLAY_PIN |
static int |
PAIRING_VARIANT_OOB_CONSENT |
static int |
PAIRING_VARIANT_PASSKEY |
static int |
PAIRING_VARIANT_PASSKEY_CONFIRMATION |
static int |
PAIRING_VARIANT_PIN |
| Constructor and Description |
|---|
BleManager(android.content.Context context)
The manager constructor.
|
BleManager(android.content.Context context,
android.os.Handler handler)
The manager constructor.
|
| Modifier and Type | Method and Description |
|---|---|
protected RequestQueue |
beginAtomicRequestQueue()
Creates an atomic request queue.
|
protected ReliableWriteRequest |
beginReliableWrite()
Begins the Reliable Write sub-procedure.
|
protected void |
cancelQueue()
Removes all enqueued requests from the queue.
|
void |
close()
Closes and releases resources.
|
ConnectRequest |
connect(android.bluetooth.BluetoothDevice device)
Creates a Connect request that will try to connect to the given Bluetooth LE device.
|
ConnectRequest |
connect(android.bluetooth.BluetoothDevice device,
int phy)
Deprecated.
Use
connect(BluetoothDevice) instead and set preferred PHY using
ConnectRequest.usePreferredPhy(int). |
protected Request |
createBond()
Deprecated.
Use
createBondInsecure() or ensureBond() instead.
Deprecated in 2.2.1. |
protected Request |
createBondInsecure()
Returns a request to create bond with the device.
|
protected void |
disableBatteryLevelNotifications()
Deprecated.
Use
disableNotifications(BluetoothGattCharacteristic) instead. |
protected WriteRequest |
disableIndications(android.bluetooth.BluetoothGattCharacteristic characteristic)
Disables indications on given characteristic.
|
protected WriteRequest |
disableNotifications(android.bluetooth.BluetoothGattCharacteristic characteristic)
Disables notifications on given characteristic.
|
DisconnectRequest |
disconnect()
Disconnects from the device or cancels the pending connection attempt.
|
protected void |
enableBatteryLevelNotifications()
Deprecated.
|
protected WriteRequest |
enableIndications(android.bluetooth.BluetoothGattCharacteristic characteristic)
Enables indications on given characteristic.
|
protected WriteRequest |
enableNotifications(android.bluetooth.BluetoothGattCharacteristic characteristic)
Enables notifications on given characteristic.
|
protected void |
enqueue(Request request)
Deprecated.
This way of enqueueing requests is deprecated, use above methods instead.
|
protected Request |
ensureBond()
Returns a request to ensure the device is bonded and link is encrypted.
|
int |
getBatteryValue()
Deprecated.
Keep the battery level in your manager instead.
|
android.bluetooth.BluetoothDevice |
getBluetoothDevice()
Returns the Bluetooth device object used in
connect(BluetoothDevice). |
int |
getConnectionState()
Method returns the connection state:
STATE_CONNECTING,
STATE_CONNECTED,
STATE_DISCONNECTING,
STATE_DISCONNECTED |
protected android.content.Context |
getContext()
Returns the context that the manager was created with.
|
protected abstract BleManager.BleManagerGattCallback |
getGattCallback()
This method must return the GATT callback used by the manager.
|
protected int |
getMtu()
Returns the current MTU (Maximum Transfer Unit).
|
protected int |
getServiceDiscoveryDelay(boolean bonded)
The onConnectionStateChange event is triggered just after the Android connects to a device.
|
protected boolean |
isBonded()
Returns whether the target device is bonded.
|
boolean |
isConnected()
This method returns true if the device is connected.
|
boolean |
isReady()
Returns true if the device is connected and the initialization has finished,
that is when
BleManagerHandler.onDeviceReady() was called. |
protected boolean |
isReliableWriteInProgress()
Returns true if
BluetoothGatt.beginReliableWrite() has been called and
the Reliable Write hasn't been executed nor aborted yet. |
void |
log(int priority,
int messageRes,
java.lang.Object... params)
Logs the given message with given log priority into the all managed devices' log session.
|
void |
log(int priority,
java.lang.String message)
Logs the given message with given log priority into the all managed devices' log session.
|
protected void |
onPairingRequestReceived(android.bluetooth.BluetoothDevice device,
int variant)
This method will be called if a remote device requires a non-'just works' pairing.
|
protected void |
overrideMtu(int mtu)
This method overrides the MTU value.
|
protected void |
readBatteryLevel()
Deprecated.
Use
readCharacteristic(BluetoothGattCharacteristic) instead. |
protected ReadRequest |
readCharacteristic(android.bluetooth.BluetoothGattCharacteristic characteristic)
Sends the read request to the given characteristic.
|
protected ReadRequest |
readDescriptor(android.bluetooth.BluetoothGattDescriptor descriptor)
Sends the read request to the given descriptor.
|
protected PhyRequest |
readPhy()
Reads the current PHY for this connections.
|
protected ReadRssiRequest |
readRssi()
Reads the current RSSI (Received Signal Strength Indication).
|
protected Request |
refreshDeviceCache()
Refreshes the device cache.
|
protected Request |
removeBond()
Enqueues removing bond information.
|
protected void |
removeIndicationCallback(android.bluetooth.BluetoothGattCharacteristic characteristic)
Removes the indications callback set using
setIndicationCallback(BluetoothGattCharacteristic). |
protected void |
removeNotificationCallback(android.bluetooth.BluetoothGattCharacteristic characteristic)
Removes the notifications callback set using
setNotificationCallback(BluetoothGattCharacteristic). |
protected void |
removeWriteCallback(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
Removes the write callback set using
setWriteCallback(BluetoothGattCharacteristic). |
protected void |
removeWriteCallback(android.bluetooth.BluetoothGattDescriptor serverDescriptor)
Removes the write callback set using
setWriteCallback(BluetoothGattCharacteristic). |
protected ConnectionPriorityRequest |
requestConnectionPriority(int priority)
Requests the new connection priority.
|
protected MtuRequest |
requestMtu(int mtu)
Requests new MTU.
|
protected void |
runOnCallbackThread(java.lang.Runnable runnable)
Runs the given runnable using a handler given to the constructor.
|
protected WriteRequest |
sendIndication(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data)
Sends the indication from the server characteristic.
|
protected WriteRequest |
sendIndication(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data,
int offset,
int length)
Sends the indication with at most length bytes from offset at given data from the server
characteristic.
|
protected WriteRequest |
sendIndication(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
Data data)
Sends the indication from the server characteristic.
|
protected WriteRequest |
sendNotification(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data)
Sends the notification from the server characteristic.
|
protected WriteRequest |
sendNotification(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data,
int offset,
int length)
Sends the notification with at most length bytes from offset at given data from the server
characteristic.
|
protected WriteRequest |
sendNotification(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
Data data)
Sends the notification from the server characteristic.
|
void |
setBondingObserver(BondingObserver callback)
Sets the observer, that will receive events related to bonding.
|
protected SetValueRequest |
setCharacteristicValue(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data)
Sets the given data to the readable server characteristic.
|
protected SetValueRequest |
setCharacteristicValue(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data,
int offset,
int length)
Sets the given data to the readable server characteristic.
|
protected SetValueRequest |
setCharacteristicValue(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
Data data)
Sets the given data to the readable server characteristic.The data will be available to be
read by the remote device.
|
void |
setConnectionObserver(ConnectionObserver callback)
Sets the connection observer.
|
protected SetValueRequest |
setDescriptorValue(android.bluetooth.BluetoothGattDescriptor serverDescriptor,
byte[] data)
Sets the given data to the readable server descriptor.
|
protected SetValueRequest |
setDescriptorValue(android.bluetooth.BluetoothGattDescriptor serverDescriptor,
byte[] data,
int offset,
int length)
Sets the given data to the readable server descriptor.
|
protected SetValueRequest |
setDescriptorValue(android.bluetooth.BluetoothGattDescriptor serverDescriptor,
Data data)
Sets the given data to the readable server descriptor.
|
void |
setGattCallbacks(BleManagerCallbacks callbacks)
Deprecated.
Use per-request callbacks.
|
protected ValueChangedCallback |
setIndicationCallback(android.bluetooth.BluetoothGattCharacteristic characteristic)
Returns the callback that is registered for value changes (indications) of given
characteristic.
|
protected ValueChangedCallback |
setNotificationCallback(android.bluetooth.BluetoothGattCharacteristic characteristic)
Returns the callback that is registered for value changes (notifications) of given
characteristic.
|
protected PhyRequest |
setPreferredPhy(int txPhy,
int rxPhy,
int phyOptions)
Enqueues a request to set the preferred PHY.
|
protected ValueChangedCallback |
setWriteCallback(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
Returns the callback that is registered for value changes (write command or write request
initiated by the remote device) of given characteristic.
|
protected ValueChangedCallback |
setWriteCallback(android.bluetooth.BluetoothGattDescriptor serverDescriptor)
Returns the callback that is registered for value changes (write command or write request
initiated by the remote device) of given descriptor.
|
protected boolean |
shouldAutoConnect()
Deprecated.
Use
ConnectRequest.useAutoConnect(boolean) instead. |
protected boolean |
shouldClearCacheWhenDisconnected()
Returns whether the device cache should be cleared after the device disconnected,
before calling
BluetoothGatt.close(). |
protected SleepRequest |
sleep(long delay)
Enqueues a sleep operation with given duration.
|
void |
useServer(BleServerManager server)
This method binds the manager with the give server instance.
|
protected WaitForValueChangedRequest |
waitForIndication(android.bluetooth.BluetoothGattCharacteristic characteristic)
Sets a one-time callback that will be notified when the value of the given characteristic
changes.
|
protected WaitForValueChangedRequest |
waitForNotification(android.bluetooth.BluetoothGattCharacteristic characteristic)
Sets a one-time callback that will be notified when the value of the given characteristic
changes.
|
protected WaitForReadRequest |
waitForRead(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
Waits until the given characteristic is read by the remote device.
|
protected WaitForReadRequest |
waitForRead(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data)
Sets the given data to the readable server characteristic and waits until they are read by the
remote device.
|
protected WaitForReadRequest |
waitForRead(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic,
byte[] data,
int offset,
int length)
Sets the given data to the readable server characteristic and waits until they are read by the
remote device.
|
protected WaitForReadRequest |
waitForRead(android.bluetooth.BluetoothGattDescriptor serverDescriptor)
Waits until the given descriptor is read by the remote device.
|
protected WaitForReadRequest |
waitForRead(android.bluetooth.BluetoothGattDescriptor serverDescriptor,
byte[] data)
Sets the given data to the readable server descriptor and waits until they are read by the
remote device.
|
protected WaitForReadRequest |
waitForRead(android.bluetooth.BluetoothGattDescriptor serverDescriptor,
byte[] data,
int offset,
int length)
Sets the given data to the readable server descriptor and waits until they are read by the
remote device.
|
protected WaitForValueChangedRequest |
waitForWrite(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
Sets a one-time callback that will be notified when the value of the given characteristic
changes.
|
protected WaitForValueChangedRequest |
waitForWrite(android.bluetooth.BluetoothGattDescriptor serverDescriptor)
Sets a one-time callback that will be notified when the value of the given descriptor
changes.
|
protected ConditionalWaitRequest<java.lang.Void> |
waitIf(ConditionalWaitRequest.Condition<java.lang.Void> condition)
Creates a conditional wait request that will wait if the given condition is satisfied.
|
protected <T> ConditionalWaitRequest<T> |
waitIf(T parameter,
ConditionalWaitRequest.Condition<T> condition)
Creates a conditional wait request that will wait if the given condition is satisfied.
|
protected ConditionalWaitRequest<java.lang.Void> |
waitUntil(ConditionalWaitRequest.Condition<java.lang.Void> condition)
Creates a conditional wait request that will wait until the given condition is not satisfied.
|
protected <T> ConditionalWaitRequest<T> |
waitUntil(T parameter,
ConditionalWaitRequest.Condition<T> condition)
Creates a conditional wait request that will wait until the given condition is not satisfied.
|
protected ConditionalWaitRequest<android.bluetooth.BluetoothGattCharacteristic> |
waitUntilIndicationsEnabled(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
Creates a request that will wait for enabling indications.
|
protected ConditionalWaitRequest<android.bluetooth.BluetoothGattCharacteristic> |
waitUntilNotificationsEnabled(android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
Creates a request that will wait for enabling notifications.
|
protected WriteRequest |
writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic characteristic,
byte[] data)
Writes the given data to the characteristic.
|
protected WriteRequest |
writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic characteristic,
byte[] data,
int offset,
int length)
Writes at most length bytes from offset at given data to the characteristic.
|
protected WriteRequest |
writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic characteristic,
Data data)
Writes the given data to the characteristic.
|
protected WriteRequest |
writeDescriptor(android.bluetooth.BluetoothGattDescriptor descriptor,
byte[] data)
Writes the given data to the descriptor.
|
protected WriteRequest |
writeDescriptor(android.bluetooth.BluetoothGattDescriptor descriptor,
byte[] data,
int offset,
int length)
Writes at most length bytes from offset at given data to the descriptor.
|
protected WriteRequest |
writeDescriptor(android.bluetooth.BluetoothGattDescriptor descriptor,
Data data)
Writes the given data to the descriptor.
|
public static final int PAIRING_VARIANT_PIN
public static final int PAIRING_VARIANT_PASSKEY
public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION
public static final int PAIRING_VARIANT_CONSENT
public static final int PAIRING_VARIANT_DISPLAY_PASSKEY
public static final int PAIRING_VARIANT_DISPLAY_PIN
public static final int PAIRING_VARIANT_OOB_CONSENT
@Deprecated protected BleManagerCallbacks callbacks
setGattCallbacks(BleManagerCallbacks).public BleManager(@NonNull
android.content.Context context)
To connect a device, call connect(BluetoothDevice).
context - the context.public BleManager(@NonNull
android.content.Context context,
@NonNull
android.os.Handler handler)
To connect a device, call connect(BluetoothDevice).
context - the context.handler - the handler used for delaying operations, timeouts and, most of all, the
request callbacks (done/fail/with, etc).public void close()
disconnect(). When the device disconnected with link loss and
ConnectRequest.shouldAutoConnect() returned true you have to call this method to
close the connection.protected void runOnCallbackThread(@NonNull
java.lang.Runnable runnable)
runnable - the runnable to be executed.@Deprecated
public void setGattCallbacks(@NonNull
BleManagerCallbacks callbacks)
callbacks - the callback listener.public final void setConnectionObserver(@Nullable
ConnectionObserver callback)
BleManager(Context, Handler).callback - the callback listener.public final void setBondingObserver(@Nullable
BondingObserver callback)
BleManager(Context, Handler).callback - the callback.BondingObserverpublic final void useServer(@NonNull
BleServerManager server)
server - the server instance.protected void onPairingRequestReceived(@NonNull
android.bluetooth.BluetoothDevice device,
int variant)
device - the device.variant - pairing variant.@NonNull protected abstract BleManager.BleManagerGattCallback getGattCallback()
@NonNull protected final android.content.Context getContext()
@Nullable public android.bluetooth.BluetoothDevice getBluetoothDevice()
connect(BluetoothDevice).connect(BluetoothDevice) wasn't called.public final boolean isConnected()
public final boolean isReady()
BleManagerHandler.onDeviceReady() was called.protected final boolean isBonded()
public final int getConnectionState()
STATE_CONNECTING,
STATE_CONNECTED,
STATE_DISCONNECTING,
STATE_DISCONNECTED@Deprecated public final int getBatteryValue()
The value returned will be invalid if overridden readBatteryLevel() and
enableBatteryLevelNotifications() were used.
public void log(int priority,
@NonNull
java.lang.String message)
ILoggerpublic void log(int priority,
@StringRes
int messageRes,
@Nullable
java.lang.Object... params)
ILogger@Deprecated protected boolean shouldAutoConnect()
ConnectRequest.useAutoConnect(boolean) instead.BleManagerCallbacks.onLinkLossOccurred(BluetoothDevice) callback is called instead of
BleManagerCallbacks.onDeviceDisconnected(BluetoothDevice).
This feature works much better on newer Android phone models and many not work 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 device with private resolvable address will fail.
The first connection to a device will always be created with autoConnect flag to false
(see BluetoothDevice.connectGatt(Context, boolean, BluetoothGattCallback)). This is
to make it quick as the user most probably waits for a quick response.
However, if this method returned true during first connection and the link was lost,
the manager will try to reconnect to it using BluetoothGatt.connect() which forces
autoConnect to true.
protected boolean shouldClearCacheWhenDisconnected()
BluetoothGatt.close(). By default it returns false.
If the returned value is true, the next time the Android device will connect to this peripheral the services will be discovered again. If false, the services will be obtained from the cache.
Note, that the BluetoothGatt#refresh() method is not in the public API and it
is not recommended to use this. However, as Android is caching services of all devices,
even if they are not bonded and have Service Changed characteristic, it may necessary to
clear the cache manually.
On older Android versions clearing device cache helped with connection stability. It was common to get error 133 on the second and following connections when services were obtained from the cache. However, full service discovery takes time and consumes peripheral's battery.
protected int getServiceDiscoveryDelay(boolean bonded)
If the gatt.discoverServices() method would be invoked here with no delay, if would return cached services, as the SC indication wouldn't be received yet. Therefore, we have to postpone the service discovery operation until we are (almost, as there is no such callback) sure, that it has been handled. It should be greater than the time from LLCP Feature Exchange to ATT Write for Service Change indication.
If your device does not use Service Change indication (for example does not have DFU) the delay may be 0.
Please calculate the proper delay that will work in your solution.
For devices that are not bonded, but support pairing, a small delay is required on some older Android versions (Nexus 4 with Android 5.1.1) when the device will send pairing request just after connection. If so, we want to wait with the service discovery until bonding is complete.
The default this implementation returns 1600 ms for bonded and 300 ms when the device is not bonded to be compatible with older versions of the library.
@NonNull public final ConnectRequest connect(@NonNull android.bluetooth.BluetoothDevice device)
TimeoutableRequest.enqueue() or TimeoutableRequest.await() in order to execute
the request.
This method returns a ConnectRequest which can be used to set completion
and failure callbacks. The completion callback (done) will be called after the initialization
is complete, after BleManagerCallbacks.onDeviceReady(BluetoothDevice) has been
called.
Calling TimeoutableRequest.await() will make this request
synchronous (the callbacks set will be ignored, instead the synchronous method will
return or throw an exception).
For asynchronous call usage, TimeoutableRequest.enqueue() must be called on the returned
request.
device - a device to connect to.@NonNull @Deprecated public final ConnectRequest connect(@NonNull android.bluetooth.BluetoothDevice device, int phy)
connect(BluetoothDevice) instead and set preferred PHY using
ConnectRequest.usePreferredPhy(int).TimeoutableRequest.enqueue() or TimeoutableRequest.await()
in order to execute the request.
This method returns a ConnectRequest which can be used to set completion
and failure callbacks. The completion callback will be called after the initialization
is complete, after BleManagerCallbacks.onDeviceReady(BluetoothDevice) has been
called.
Calling TimeoutableRequest.await() will make this request
synchronous (the callbacks set will be ignored, instead the synchronous method will
return or throw an exception).
For asynchronous call usage, TimeoutableRequest.enqueue() must be called on the returned
request.
device - a device to connect to.phy - preferred PHY for connections to remote LE device. Bitwise OR of any of
PhyRequest.PHY_LE_1M_MASK, PhyRequest.PHY_LE_2M_MASK,
and PhyRequest.PHY_LE_CODED_MASK. This option does not take effect
if autoConnect is set to true. PHY 2M and Coded are supported
on newer devices running Android Oreo or newer.@NonNull public final DisconnectRequest disconnect()
@Deprecated @NonNull protected Request createBond()
createBondInsecure() or ensureBond() instead.
Deprecated in 2.2.1.connect(BluetoothDevice) which will try to connect to the device.
If you need to pair with a device before connecting to it you may do it without
the use of BleManager object and connect after bond is established.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
Important: This request does NOT guarantee that the link will be encrypted. If the bond information is present on the phone, but was removed from the peripheral (or another peripheral is pretending to be the one) this request will succeed, as it immediately returns if the bond information is present on the Android client. To make sure no sensitive information is stolen, protect your characteristics and/or descriptors by assigning them security level. Also, clearly inform user that a device is being bonded to avoid MITM.
ensureBond(),
createBondInsecure()@NonNull protected Request createBondInsecure()
connect(BluetoothDevice) which will try to connect to the device.
If you need to pair with a device before connecting to it you may do it without
the use of BleManager object and connect after bond is established.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
Important: This request does NOT guarantee that the link will be encrypted. If the bond information is present on the phone, but was removed from the peripheral (or another peripheral is pretending to be the one) this request will succeed, as it immediately returns if the bond information is present on the Android client. To make sure no sensitive information is stolen, protect your characteristics and/or descriptors by assigning them security level. Also, clearly inform user which device is being bonded to avoid MITM.
To ensure link encryption, use ensureBond().
ensureBond()protected Request ensureBond()
BluetoothDevice.getBondState() returns true
even if the link is not encrypted, or the device is not connected at all, checking only
if the bond information is present on Android. Moreover, calling
BluetoothDevice.createBond() returns false if bond is already present on Android,
despite not being used, giving no trustworthy method to ensure that link is encrypted.
This method will always call BluetoothDevice.createBond(). If this method returns
false (e.g. because the bond information is already present on Android), this will remove the
current bond information and call BluetoothDevice.createBond() again.
Important: This may fail, if:
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
@NonNull protected Request removeBond()
The success callback will be called after the device get disconnected,
when the BluetoothDevice.getBondState() changes to BluetoothDevice.BOND_NONE.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
@NonNull protected ValueChangedCallback setNotificationCallback(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
enableNotifications(BluetoothGattCharacteristic).
This applies also when they were already enabled on the remote side.
To remove the callback, call
removeNotificationCallback(BluetoothGattCharacteristic).
characteristic - characteristic to bind the callback with. If null, the returned
callback will not be null, but will not be used.@NonNull protected ValueChangedCallback setIndicationCallback(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
enableIndications(BluetoothGattCharacteristic).
This applies also when they were already enabled on the remote side.
To remove the callback, call
removeIndicationCallback(BluetoothGattCharacteristic).
characteristic - characteristic to bind the callback with. If null, the returned
callback will not be null, but will not be used.@NonNull protected ValueChangedCallback setWriteCallback(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
To remove the callback, call
removeWriteCallback(BluetoothGattCharacteristic).
serverCharacteristic - characteristic to bind the callback with. If null, the returned
callback will not be null, but will not be used.@NonNull protected ValueChangedCallback setWriteCallback(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor)
To remove the callback, call
removeWriteCallback(BluetoothGattCharacteristic).
serverDescriptor - descriptor to bind the callback with. If null, the returned
callback will not be null, but will not be used.protected void removeNotificationCallback(@Nullable
android.bluetooth.BluetoothGattCharacteristic characteristic)
setNotificationCallback(BluetoothGattCharacteristic).characteristic - characteristic to unbind the callback from.protected void removeIndicationCallback(@Nullable
android.bluetooth.BluetoothGattCharacteristic characteristic)
setIndicationCallback(BluetoothGattCharacteristic).characteristic - characteristic to unbind the callback from.protected void removeWriteCallback(@Nullable
android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
setWriteCallback(BluetoothGattCharacteristic).serverCharacteristic - characteristic to unbind the callback from.protected void removeWriteCallback(@Nullable
android.bluetooth.BluetoothGattDescriptor serverDescriptor)
setWriteCallback(BluetoothGattCharacteristic).serverDescriptor - descriptor to unbind the callback from.@NonNull protected WaitForValueChangedRequest waitForNotification(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
If WaitForValueChangedRequest.merge(DataMerger) was used, the whole message will be
completed before the callback is notified.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic that value is expect to change.@NonNull protected WaitForValueChangedRequest waitForIndication(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
If WaitForValueChangedRequest.merge(DataMerger) was used, the whole message will be
completed before the callback is notified.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic that value is expect to change.@NonNull protected WaitForValueChangedRequest waitForWrite(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
If WaitForValueChangedRequest.merge(DataMerger) was used, the whole message will be
completed before the callback is notified.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic that is expected to be written.@NonNull protected WaitForValueChangedRequest waitForWrite(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor)
If WaitForValueChangedRequest.merge(DataMerger) was used, the whole message will be
completed before the callback is notified.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the descriptor that is expected to be written.@NonNull protected ConditionalWaitRequest<java.lang.Void> waitIf(@NonNull ConditionalWaitRequest.Condition<java.lang.Void> condition)
condition - The condition to examine. If it's satisfied, the manager will wait.@NonNull protected <T> ConditionalWaitRequest<T> waitIf(@Nullable T parameter, @NonNull ConditionalWaitRequest.Condition<T> condition)
parameter - An optional parameter that will be passed to the condition.condition - The condition to examine. If it's satisfied, the manager will wait.@NonNull protected ConditionalWaitRequest<java.lang.Void> waitUntil(@NonNull ConditionalWaitRequest.Condition<java.lang.Void> condition)
condition - The condition to examine. If it's not satisfied, the manager will wait.@NonNull protected <T> ConditionalWaitRequest<T> waitUntil(@Nullable T parameter, @NonNull ConditionalWaitRequest.Condition<T> condition)
parameter - An optional parameter that will be passed to the condition.condition - The condition to examine. If it's not satisfied, the manager will wait.@NonNull protected ConditionalWaitRequest<android.bluetooth.BluetoothGattCharacteristic> waitUntilNotificationsEnabled(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
serverCharacteristic - the server characteristic with notify property.@NonNull protected ConditionalWaitRequest<android.bluetooth.BluetoothGattCharacteristic> waitUntilIndicationsEnabled(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
serverCharacteristic - the server characteristic with indicate property.@NonNull protected WaitForReadRequest waitForRead(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic)
setCharacteristicValue(BluetoothGattCharacteristic, Data) to set data, or
waitForRead(BluetoothGattCharacteristic, byte[], int, int) to set the data immediately.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the target characteristic to be read.@NonNull protected WaitForReadRequest waitForRead(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the target characteristic to be read.data - the data to be sent as read response.@NonNull protected WaitForReadRequest waitForRead(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data, int offset, int length)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the target characteristic to be read.data - the data buffer.offset - index of the first byte to be returned.length - number of bytes to be returned.@NonNull protected WaitForReadRequest waitForRead(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor)
setDescriptorValue(BluetoothGattDescriptor, byte[]) to set data, or
waitForRead(BluetoothGattDescriptor, byte[], int, int) to set the data immediately.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the target descriptor to be read.@NonNull protected WaitForReadRequest waitForRead(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor, @Nullable byte[] data)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the target descriptor to be read.data - the data to be sent as read response.@NonNull protected WaitForReadRequest waitForRead(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor, @Nullable byte[] data, int offset, int length)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the target descriptor to be read.data - the data buffer.offset - index of the first byte to be returned.length - number of bytes to be returned.@NonNull protected SetValueRequest setCharacteristicValue(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable Data data)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the target characteristic to set value.data - data to be set.@NonNull protected SetValueRequest setCharacteristicValue(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the target characteristic to set value.data - data to be set.@NonNull protected SetValueRequest setCharacteristicValue(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data, int offset, int length)
sendNotification(BluetoothGattCharacteristic, byte[]) or #sendIn
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the target characteristic to set value.data - data to be set.offset - index of the first byte to be set.length - number of bytes to be set.@NonNull protected SetValueRequest setDescriptorValue(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor, @Nullable Data data)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the target descriptor to set value.data - data to be set.@NonNull protected SetValueRequest setDescriptorValue(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor, @Nullable byte[] data)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the target descriptor to set value.data - data to be set.@NonNull protected SetValueRequest setDescriptorValue(@Nullable android.bluetooth.BluetoothGattDescriptor serverDescriptor, @Nullable byte[] data, int offset, int length)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverDescriptor - the target descriptor to set value.data - data to be set.offset - index of the first byte to be set.length - number of bytes to be set.@NonNull protected WriteRequest enableNotifications(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to be enabled.@NonNull protected WriteRequest disableNotifications(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to be disabled.@NonNull protected WriteRequest enableIndications(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to be enabled.@NonNull protected WriteRequest disableIndications(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to be disabled.@NonNull protected ReadRequest readCharacteristic(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic)
fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to read.@NonNull protected WriteRequest writeCharacteristic(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic, @Nullable Data data)
Use split() or
split(DataSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to write to.data - data to be written to the characteristic.@NonNull protected WriteRequest writeCharacteristic(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic, @Nullable byte[] data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to write to.data - data to be written to the characteristic.@NonNull protected WriteRequest writeCharacteristic(@Nullable android.bluetooth.BluetoothGattCharacteristic characteristic, @Nullable byte[] data, int offset, int length)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
characteristic - the characteristic to write to.data - data to be written to the characteristic.offset - index of the first byte to be sent.length - number of bytes to be sent.@NonNull protected ReadRequest readDescriptor(@Nullable android.bluetooth.BluetoothGattDescriptor descriptor)
fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
descriptor - the descriptor to read.@NonNull protected WriteRequest writeDescriptor(@Nullable android.bluetooth.BluetoothGattDescriptor descriptor, @Nullable Data data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the descriptor is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
descriptor - the descriptor to write to.data - data to be written to the descriptor.@NonNull protected WriteRequest writeDescriptor(@Nullable android.bluetooth.BluetoothGattDescriptor descriptor, @Nullable byte[] data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the descriptor is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
descriptor - the descriptor to write to.data - data to be written to the descriptor.@NonNull protected WriteRequest writeDescriptor(@Nullable android.bluetooth.BluetoothGattDescriptor descriptor, @Nullable byte[] data, int offset, int length)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the descriptor is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
descriptor - the descriptor to write to.data - data to be written to the descriptor.offset - index of the first byte to be sent.length - number of bytes to be sent.@NonNull protected WriteRequest sendNotification(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable Data data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic to notify.data - data to be sent to the characteristic.@NonNull protected WriteRequest sendNotification(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic to notify.data - data to be sent to the characteristic.@NonNull protected WriteRequest sendNotification(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data, int offset, int length)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic to notify.data - the data buffer.offset - index of the first byte to be sent.length - number of bytes to be sent.@NonNull protected WriteRequest sendIndication(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable Data data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic to indicate.data - data to be sent.@NonNull protected WriteRequest sendIndication(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic to indicate.data - data to be sent.@NonNull protected WriteRequest sendIndication(@Nullable android.bluetooth.BluetoothGattCharacteristic serverCharacteristic, @Nullable byte[] data, int offset, int length)
Use split() or
split(ValueSplitter) on the returned
WriteRequest if data should be automatically split into multiple packets.
If the characteristic is null, the fail(FailCallback)
callback will be called.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
serverCharacteristic - the characteristic to indicate.data - the data buffer.offset - index of the first byte to be sent.length - number of bytes to be sent.@NonNull protected RequestQueue beginAtomicRequestQueue()
@NonNull protected ReliableWriteRequest beginReliableWrite()
ReliableWriteRequest.add(Operation) instead of using
Request.enqueue(). The library will verify all Write operations and will
automatically abort the Reliable Write procedure when the returned data mismatch with the
data sent. When all requests enqueued in the ReliableWriteRequest were completed,
the Reliable Write will be automatically executed.
Long Write will not work when Reliable Write is in progress. The library will make sure
that WriteRequest.split() was called for all WriteRequest packets, had
they not been assigned other splitter.
At least one Write operation must be executed before executing or aborting, otherwise the
GattError.GATT_INVALID_OFFSET error will be reported. Because of that, enqueueing
a ReliableWriteRequest without any operations does nothing.
Example of usage:
beginReliableWrite()
.add(writeCharacteristic(someCharacteristic, someData)
.fail(...)
.done(...))
// Non-write requests are also possible
.add(requestMtu(200))
// Data will be written in the same order
.add(writeCharacteristic(someCharacteristic, differentData))
// This will return the OLD data, not 'differentData', as the RW wasn't executed!
.add(readCharacteristic(someCharacteristic).with(callback))
// Multiple characteristics may be written during a single RW
.add(writeCharacteristic(someOtherCharacteristic, importantData))
// Finally, enqueue the Reliable Write request in BleManager
.enqueue();
protected final boolean isReliableWriteInProgress()
BluetoothGatt.beginReliableWrite() has been called and
the Reliable Write hasn't been executed nor aborted yet.@Deprecated protected void readBatteryLevel()
readCharacteristic(BluetoothGattCharacteristic) instead.@Deprecated protected void enableBatteryLevelNotifications()
setNotificationCallback(BluetoothGattCharacteristic) and
enableNotifications(BluetoothGattCharacteristic) instead.@Deprecated protected void disableBatteryLevelNotifications()
disableNotifications(BluetoothGattCharacteristic) instead.protected MtuRequest requestMtu(int mtu)
MtuCallback.onMtuChanged(BluetoothDevice, int) set with
MtuRequest.with(MtuCallback) will be called with current MTU value.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
protected int getMtu()
requestMtu(int) and a successful callback is received. If the peripheral requests
MTU change, the BluetoothGattCallback.onMtuChanged(BluetoothGatt, int, int)
callback is not invoked, therefor the returned MTU value will not be correct.
Use BluetoothGattServerCallback.onMtuChanged(BluetoothDevice, int)
to get the callback with right value requested from the peripheral side.protected void overrideMtu(int mtu)
BluetoothGattServerCallback.onMtuChanged(BluetoothDevice, int)
callback. If you want to set MTU as a master, use requestMtu(int) instead.mtu - the MTU value set by the peripheral.protected ConnectionPriorityRequest requestConnectionPriority(int priority)
BluetoothGatt.CONNECTION_PRIORITY_HIGH
- Interval: 11.25 -15 ms, latency: 0, supervision timeout: 20 sec,BluetoothGatt.CONNECTION_PRIORITY_BALANCED
- Interval: 30 - 50 ms, latency: 0, supervision timeout: 20 sec,BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER
- Interval: 100 - 125 ms, latency: 2, supervision timeout: 20 sec.Request.fail(FailCallback) callback or throw
RequestFailedException with
FailCallback.REASON_REQUEST_FAILED status if called synchronously.
Starting from Android Oreo you may get a callback with the interval, latency and timeout
using ConnectionPriorityRequest.with(ConnectionPriorityCallback).
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
priority - one of: BluetoothGatt.CONNECTION_PRIORITY_HIGH,
BluetoothGatt.CONNECTION_PRIORITY_BALANCED,
BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER.protected PhyRequest setPreferredPhy(int txPhy, int rxPhy, int phyOptions)
PHY LE 2M and PHY LE Coded are supported only on Android Oreo or newer. You may safely request other PHYs on older platforms, but you will get PHY LE 1M as TX and RX PHY in the callback.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
txPhy - preferred transmitter PHY. Bitwise OR of any of
PhyRequest.PHY_LE_1M_MASK, PhyRequest.PHY_LE_2M_MASK,
and PhyRequest.PHY_LE_CODED_MASK.rxPhy - preferred receiver PHY. Bitwise OR of any of
PhyRequest.PHY_LE_1M_MASK, PhyRequest.PHY_LE_2M_MASK,
and PhyRequest.PHY_LE_CODED_MASK.phyOptions - preferred coding to use when transmitting on the LE Coded PHY. Can be one
of PhyRequest.PHY_OPTION_NO_PREFERRED,
PhyRequest.PHY_OPTION_S2 or PhyRequest.PHY_OPTION_S8.protected PhyRequest readPhy()
PHY LE 2M and PHY LE Coded are supported only on Android Oreo or newer. You may safely read PHY on older platforms, but you will get PHY LE 1M as TX and RX PHY in the callback.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
protected ReadRssiRequest readRssi()
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
protected Request refreshDeviceCache()
BluetoothGatt#refresh() method is not in the
public API (it's hidden, and on Android P it is on a light gray list) it is called
using reflections and may be removed in some future Android release or on some devices.
There is no callback indicating when the cache has been cleared. This library assumes
some time and waits. After the delay, it will start service discovery and clear the
task queue. When the service discovery finishes, the
BleManagerHandler.isRequiredServiceSupported(BluetoothGatt) and
BleManagerHandler.isOptionalServiceSupported(BluetoothGatt) will
be called and the initialization will be performed as if the device has just connected.
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
protected SleepRequest sleep(long delay)
The returned request must be either enqueued using Request.enqueue() for
asynchronous use, or awaited using await() in synchronous execution.
delay - the delay in milliseconds.@Deprecated
protected final void enqueue(@NonNull
Request request)
request - the new request to be added to the end of the queue.protected final void cancelQueue()
FailCallback.REASON_CANCELLED.
If a BLE operation was in progress when the queue was cancelled, enqueueing a next BLE
operation immediately may cause the Bluetooth to behave improperly, as the manager will
try to execute it without waiting for the callback. A delay
in such case is recommended.