+ * This method is called when the services has been discovered and the device is supported + * (has required service). + *
+ * Remember to call {@link Request#enqueue()} for each request. + *
+ * A sample initialization should look like this: + *
+ * @Override
+ * protected void initialize() {
+ * requestMtu(MTU)
+ * .with((device, mtu) -> {
+ * ...
+ * })
+ * .enqueue();
+ * setNotificationCallback(characteristic)
+ * .with((device, data) -> {
+ * ...
+ * });
+ * enableNotifications(characteristic)
+ * .done(device -> {
+ * ...
+ * })
+ * .fail((device, status) -> {
+ * ...
+ * })
+ * .enqueue();
+ * }
+ *
+ */
+ protected void initialize() {
+ // Don't call super.initialize() when overriding this method.
+ requestHandler.initialize();
+ }
+
+ /**
+ * This method should return true when the gatt device supports the
+ * required services.
+ *
+ * @param gatt the gatt device with services discovered
+ * @return True when the device has the required service.
+ */
+ protected boolean isRequiredServiceSupported(@NonNull final BluetoothGatt gatt) {
+ // Don't call super.isRequiredServiceSupported(gatt) when overriding this method.
+ return requestHandler.isRequiredServiceSupported(gatt);
+ }
+
+ /**
+ * This method should return true when the gatt device supports the
+ * optional services. The default implementation returns false.
+ *
+ * @param gatt the gatt device with services discovered
+ * @return True when the device has the optional service.
+ */
+ protected boolean isOptionalServiceSupported(@NonNull final BluetoothGatt gatt) {
+ // Don't call super.isOptionalServiceSupported(gatt) when overriding this method.
+ return requestHandler.isOptionalServiceSupported(gatt);
+ }
+
+ /**
+ * In this method the manager should get references to server characteristics and descriptors
+ * that will use. The method is called after the service discovery of a remote device has
+ * finished and {@link #isRequiredServiceSupported(BluetoothGatt)} returned true.
+ * + * The references obtained in this method should be released in {@link #onServicesInvalidated()}. + *
+ * This method is called only when the server was set by + * {@link BleManager#useServer(BleServerManager)} and opened using {@link BleServerManager#open()}. + * + * @param server The GATT Server instance. Use {@link BluetoothGattServer#getService(UUID)} to + * obtain service instance. + */ + protected void onServerReady(@NonNull final BluetoothGattServer server) { + // Don't call super.onServerReady(server) when overriding this method. + requestHandler.onServerReady(server); + } + + /** + * This method should nullify all services and characteristics of the device. + *
+ * It's called when the services were invalidated and can no longer be used. Most probably the + * device has disconnected, Service Changed indication was received, or + * {@link #refreshDeviceCache()} request was executed, which has invalidated cached services. + */ + protected void onServicesInvalidated() { + // Don't call super.onServicesInvalidated() when overriding this method. + requestHandler.onServicesInvalidated(); + } + + /** + * Called when the initialization queue is complete. + */ + protected void onDeviceReady() { + // Don't call super.onDeviceReady() when overriding this method. + requestHandler.onDeviceReady(); + } + + /** + * Called each time the task queue gets cleared. + */ + protected void onManagerReady() { + // Don't call super.onManagerReady() when overriding this method. + requestHandler.onManagerReady(); + } + /** * Closes and releases resources. This method will be called automatically after * calling {@link #disconnect()}. When the device disconnected with link loss and @@ -303,16 +415,29 @@ protected void onPairingRequestReceived(@NonNull final BluetoothDevice device, } /** - * This method must return the GATT callback used by the manager. + * This method returns the GATT callback used by the manager. * - * The object must exist when this method is called, that is in the BleManager's constructor. - * Therefore, it cannot return a local field in the extending manager, as this is created after - * the constructor finishes. + * Since version 2.6 this method is private. The manager just can implement all inner methods + * directly, without additional object. * * @return The gatt callback object. + * @deprecated Implement all methods directly in your manager. */ + @Deprecated @NonNull - protected abstract BleManagerGattCallback getGattCallback(); + protected BleManagerGattCallback getGattCallback() { + return new BleManagerGattCallback() { + @Override + protected boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) { + return false; + } + + @Override + protected void onServicesInvalidated() { + // empty default implementation + } + }; + } /** * Returns the context that the manager was created with. @@ -2006,8 +2131,8 @@ protected ReadRssiRequest readRssi() { * 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 - * {@link BleManager.BleManagerGattCallback#isRequiredServiceSupported(BluetoothGatt)} and - * {@link BleManager.BleManagerGattCallback#isOptionalServiceSupported(BluetoothGatt)} will + * {@link BleManager#isRequiredServiceSupported(BluetoothGatt)} and + * {@link BleManager#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 {@link Request#enqueue()} for
diff --git a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java
index d9c49c8e..0c501e73 100644
--- a/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java
+++ b/ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java
@@ -476,7 +476,7 @@ void attachClientConnection(BluetoothDevice clientDevice) {
// Since we are opting to use server only connection, we must do this here instead.
initializeServerAttributes();
// the other path also calls this part of the callbacks
- initialize();
+ manager.initialize();
}
}
@@ -500,7 +500,7 @@ private void initializeServerAttributes() {
}
}
}
- onServerReady(server);
+ manager.onServerReady(server);
}
}
}
@@ -1693,6 +1693,7 @@ final void overrideMtu(@IntRange(from = 23, to = 517) final int mtu) {
*
* @param gatt the gatt device with services discovered
* @return
- * This method is called from the main thread when the services has been discovered and
+ * This method is called when the services has been discovered and
* the device is supported (has required service).
*
- * Remember to call {@link Request#enqueue()} for each request.
- *
- * A sample initialization should look like this:
- * True when the device has the required service.
+ * @deprecated Use {@link BleManager#isRequiredServiceSupported(BluetoothGatt)} instead.
*/
protected abstract boolean isRequiredServiceSupported(@NonNull final BluetoothGatt gatt);
@@ -1702,7 +1703,9 @@ final void overrideMtu(@IntRange(from = 23, to = 517) final int mtu) {
*
* @param gatt the gatt device with services discovered
* @return True when the device has the optional service.
+ * @deprecated Use {@link BleManager#isOptionalServiceSupported(BluetoothGatt)} instead.
*/
+ @Deprecated
protected boolean isOptionalServiceSupported(@NonNull final BluetoothGatt gatt) {
return false;
}
@@ -1733,35 +1736,12 @@ protected Deque
- * @Override
- * protected void initialize() {
- * requestMtu(MTU)
- * .with((device, mtu) -> {
- * ...
- * })
- * .enqueue();
- * setNotificationCallback(characteristic)
- * .with((device, data) -> {
- * ...
- * });
- * enableNotifications(characteristic)
- * .done(device -> {
- * ...
- * })
- * .fail((device, status) -> {
- * ...
- * })
- * .enqueue();
- * }
- *
+ * @deprecated Use {@link BleManager#initialize()} instead.
*/
+ @Deprecated
protected void initialize() {
// empty initialization queue
}
@@ -1778,21 +1758,27 @@ protected void initialize() {
*
* @param server The GATT Server instance. Use {@link BluetoothGattServer#getService(UUID)} to
* obtain service instance.
+ * @deprecated Use {@link BleManager#onServerReady(BluetoothGattServer)} instead.
*/
+ @Deprecated
protected void onServerReady(@NonNull final BluetoothGattServer server) {
// empty initialization
}
/**
* Called when the initialization queue is complete.
+ * @deprecated Use {@link BleManager#onDeviceReady()} instead.
*/
+ @Deprecated
protected void onDeviceReady() {
// empty
}
/**
* Called each time the task queue gets cleared.
+ * @deprecated Use {@link BleManager#onManagerReady()} instead.
*/
+ @Deprecated
protected void onManagerReady() {
// empty
}
@@ -1808,6 +1794,7 @@ protected void onManagerReady() {
*/
@Deprecated
protected void onDeviceDisconnected() {
+ // empty
}
/**
@@ -1817,7 +1804,9 @@ protected void onDeviceDisconnected() {
* device has disconnected, Service Changed indication was received, or
* {@link BleManager#refreshDeviceCache()} request was executed, which has invalidated cached
* services.
+ * @deprecated Use {@link BleManager#onServicesInvalidated()} instead.
*/
+ @Deprecated
protected abstract void onServicesInvalidated();
private void notifyDeviceDisconnected(@NonNull final BluetoothDevice device, final int status) {
@@ -1871,7 +1860,7 @@ private void notifyDeviceDisconnected(@NonNull final BluetoothDevice device, fin
dataProviders.clear();
batteryLevelNotificationCallback = null;
batteryValue = -1;
- onServicesInvalidated();
+ manager.onServicesInvalidated();
onDeviceDisconnected();
}
@@ -2188,10 +2177,10 @@ public void onServicesDiscovered(@NonNull final BluetoothGatt gatt, final int st
if (status == BluetoothGatt.GATT_SUCCESS) {
log(Log.INFO, () -> "Services discovered");
servicesDiscovered = true;
- if (isRequiredServiceSupported(gatt)) {
+ if (manager.isRequiredServiceSupported(gatt)) {
log(Log.VERBOSE, () -> "Primary service found");
deviceNotSupported = false;
- final boolean optionalServicesFound = isOptionalServiceSupported(gatt);
+ final boolean optionalServicesFound = manager.isOptionalServiceSupported(gatt);
if (optionalServicesFound)
log(Log.VERBOSE, () -> "Secondary service found");
@@ -2253,7 +2242,7 @@ public void onServicesDiscovered(@NonNull final BluetoothGatt gatt, final int st
}
// End
- initialize();
+ manager.initialize();
nextRequest(true);
} else {
log(Log.WARN, () -> "Device is not supported");
@@ -2287,7 +2276,7 @@ public void onServiceChanged(@NonNull final BluetoothGatt gatt) {
// Forbid enqueuing more operations.
operationInProgress = true;
// Invalidate all services and characteristics
- onServicesInvalidated();
+ manager.onServicesInvalidated();
onDeviceDisconnected();
// Clear queues, services are no longer valid.
taskQueue.clear();
@@ -2556,7 +2545,7 @@ public void onCharacteristicChanged(
// Forbid enqueuing more operations.
operationInProgress = true;
// Invalidate all services and characteristics
- onServicesInvalidated();
+ manager.onServicesInvalidated();
onDeviceDisconnected();
// Clear queues, services are no longer valid.
taskQueue.clear();
@@ -3327,7 +3316,7 @@ private synchronized void nextRequest(final boolean force) {
// will not start new nextRequest() call.
operationInProgress = true;
ready = true;
- onDeviceReady();
+ manager.onDeviceReady();
if (bluetoothDevice != null) {
postCallback(c -> c.onDeviceReady(bluetoothDevice));
postConnectionStateChange(o -> o.onDeviceReady(bluetoothDevice));
@@ -3344,7 +3333,7 @@ private synchronized void nextRequest(final boolean force) {
// No more tasks to perform
operationInProgress = false;
this.request = null;
- onManagerReady();
+ manager.onManagerReady();
return;
}
}
@@ -3702,7 +3691,7 @@ private synchronized void nextRequest(final boolean force) {
final BluetoothGatt bluetoothGatt = this.bluetoothGatt;
if (connected && bluetoothGatt != null) {
// Invalidate all services and characteristics
- onServicesInvalidated();
+ manager.onServicesInvalidated();
onDeviceDisconnected();
// And discover services again
serviceDiscoveryRequested = true;
diff --git a/moustache/README.mo b/moustache/README.mo
index e7c2b009..eaf7e8f4 100644
--- a/moustache/README.mo
+++ b/moustache/README.mo
@@ -90,6 +90,20 @@ The library uses Java 1.8 features. If you're using Android Studio below 4.2, ma
### Recent changes
+See [Releases](https://github.com/NordicSemiconductor/Android-BLE-Library/releases) for details.
+Below is short summary:
+
+Version 2.6
+
+1. `getGattCallback()` method has been deprecated. Instead, simply move the inner methods to the
+ `BleManager` class. See [this PR](https://github.com/NordicSemiconductor/Android-nRF-Blinky/pull/78).
+2. Support for *server only* implementation using `attachClientConnection(BluetoothDevice)`. Call it instead of
+ `connect(BluetoothDevice)` to use the device as server-only.
+3. Data provider for read requests (server side) was added.
+4. Cancellation support for flows and suspended methods added to `:ble-kts`.
+Version 2.4
@@ -139,15 +153,15 @@ setNotificationCallback(characteristic)
Version 2.2
-1. GATT Server support. This includes setting up the local GATT server on the Android device, new
- requests for server operations:
- * *wait for read*,
- * *wait for write*,
- * *send notification*,
+1. GATT Server support. This includes setting up the local GATT server on the Android device, new
+ requests for server operations:
+ * *wait for read*,
+ * *wait for write*,
+ * *send notification*,
* *send indication*,
* *set characteristic value*,
* *set descriptor value*.
-2. New conditional requests:
+2. New conditional requests:
* *wait if*,
* *wait until*.
3. BLE operations are no longer called from the main thread.
@@ -159,137 +173,14 @@ Check out [migration guide](MIGRATION.md).
## Usage
-A `BleManager` instance is responsible for connecting and communicating with a single peripheral.
-Having multiple instances of the manager is possible to connect with multiple devices simultaneously.
-The `BleManager` must be extended with your implementation where you define the high level device's API.
-
-```java
-class MyBleManager extends BleManager {
- private static final String TAG = "MyBleManager";
-
- private BluetoothGattCharacteristic fluxCapacitorControlPoint;
-
- public MyBleManager(@NonNull final Context context) {
- super(context);
- }
-
- @Override
- public int getMinLogPriority() {
- // Use to return minimal desired logging priority.
- return Log.VERBOSE;
- }
-
- @Override
- public void log(int priority, @NonNull String message) {
- // Log from here.
- Log.println(priority, TAG, message);
- }
-
- @NonNull
- @Override
- protected BleManagerGattCallback getGattCallback() {
- return new MyGattCallbackImpl();
- }
-
- private class MyGattCallbackImpl extends BleManagerGattCallback {
- @Override
- protected boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {
- // Here get instances of your characteristics.
- // Return false if a required service has not been discovered.
- BluetoothGattService fluxCapacitorService = gatt.getService(FLUX_SERVICE_UUID);
- if (fluxCapacitorService != null) {
- fluxCapacitorControlPoint = fluxCapacitorService.getCharacteristic(FLUX_CHAR_UUID);
- }
- return fluxCapacitorControlPoint != null;
- }
-
- @Override
- protected void initialize() {
- // Initialize your device.
- // This means e.g. enabling notifications, setting notification callbacks,
- // sometimes writing something to some Control Point.
- // Kotlin projects should not use suspend methods here, which require a scope.
- requestMtu(517)
- .enqueue();
- }
-
- @Override
- protected void onServicesInvalidated() {
- // This method is called when the services get invalidated, i.e. when the device
- // disconnects.
- // References to characteristics should be nullified here.
- fluxCapacitorControlPoint = null;
- }
- }
-
- // Here you may add some high level methods for your device:
- public void enableFluxCapacitor() {
- // Do the magic.
- writeCharacteristic(fluxCapacitorControlPoint, Flux.enable(), BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
- .enqueue()
- }
-}
-```
+See [Usage](USAGE.md) for more details.
-The [BleManager](https://github.com/NordicSemiconductor/Android-BLE-Library/blob/master/ble/src/main/java/no/nordicsemi/android/ble/BleManager.java)
-class exposes high level API for connecting and communicating with Bluetooth LE peripherals.
-
-```java
-connect(bluetoothDevice)
- // Automatic retries are supported, in case of 133 error.
- .retry(3 /* times, with */, 100 /* ms interval */)
- // A connection timeout can be set. This is additional to the Android's connection timeout which is 30 seconds.
- .timeout(15_000 /* ms */)
- // The auto connect feature from connectGatt is available as well
- .useAutoConnect(true)
- // This API can be set on any Android version, but will only be used on devices running Android 8+ with
- // support to the selected PHY.
- .usePreferredPhy(PhyRequest.PHY_LE_1M_MASK | PhyRequest.PHY_LE_2M_MASK | PhyRequest.PHY_LE_CODED_MASK)
- // A connection timeout can be also set. This is additional to the Android's connection timeout which is 30 seconds.
- .timeout(15_000 /* ms */)
- // Each request has number of callbacks called in different situations:
- .before(device -> { /* called when the request is about to be executed */ })
- .done(device -> { /* called when the device has connected, has required services and has been initialized */ })
- .fail(device, status -> { /* called when the request has failed */ })
- .then(device -> { /* called when the request was finished with either success, or a failure */ })
- // Each request must be enqueued.
- // Kotlin projects can use suspend() or suspendForResult() instead.
- // Java projects can also use await() which is blocking.
- .enqueue()
-```
+## Examples
-```java
-writeCharacteristic(someCharacteristic, someData, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
- // Outgoing data can use automatic splitting.
- .split(customSplitter, progressCallback /* optional */)
- // .split() with no parameters uses the default MTU splitter.
- // Kotlin projects can use .splitWithProgressAsFlow(customSplitter) to get the progress as Flow.
- .before(device -> { /* called when the request is about to be executed */ })
- .with(device, data -> { /* called when the request has been executed */ })
- .done(device -> { /* called when the request has completed successfully */ })
- .fail(device, status -> { /* called when the request has failed */ })
- .invalid({ /* called when the request was invalid, i.e. the target device or given characteristic was null */ })
- .then(device -> { /* called when the request was finished with either success, or a failure */ })
- // Remember to enqueue each request.
- .enqueue()
-```
+#### Trivia game
-```java
-readCharacteristic(someCharacteristic)
- // Incoming data can use automatic merging.
- .merge(customMerger, progressCallback /* optional */)
- // Kotlin projects can use .mergeWithProgressAsFlow(customMerger) to get the progress as Flow.
- // Incoming packets can also be filtered, so that not everything goes to the merger.
- .filter(dataFilter)
- // Complete, merged packets can also be filtered.
- .filterPacket(packetFilter)
- // [...]
- .with(device, data -> { /* called when the data have been received */ })
- // [...]
- // Once again, remember to enqueue each request!
- .enqueue()
-```
-All requests are automatically enqueued and executed sequentially.
+The [Trivia game](https://github.com/NordicSemiconductor/Android-BLE-Library/tree/main/examples/trivia)
+is an example demonstrating client and server.
#### GATT Client Example
@@ -300,6 +191,10 @@ demands encryption as an illustration of best-practice.
You can run this client on one device and a complimenting server on another (see the next section).
+> Note:
+ This project is not maintained actively. It is provided as an example only and may not be
+ migrated to latest version.
+
#### GATT Server example
Starting from version 2.2 you may now define and use the GATT server in the BLE Library.
@@ -309,6 +204,10 @@ server provided as a foreground service. There's a simple UI with a text field t
the value of a characteristic that can be read and subscribed to. This characteristic also
demands encryption as an illustration of best-practice.
+> Note:
+ This project is not maintained actively. It is provided as an example only and may not be
+ migrated to latest version.
+
#### More examples
Find the simple example here [Android nRF Blinky](https://github.com/NordicSemiconductor/Android-nRF-Blinky).