final void onDescriptorReadRequest(@NonNull final BluetoothGattServer server,
									   @NonNull final BluetoothDevice device, final int requestId, final int offset,
									   @NonNull final BluetoothGattDescriptor descriptor) {
		log(Log.DEBUG, () ->
				"[Server callback] Read request for descriptor " + descriptor.getUuid() +
				" (requestId=" + requestId + ", offset: " + offset + ")");
		if (offset == 0)
			log(Log.INFO, () -> "[Server] READ request for descriptor " + descriptor.getUuid() + " received");

		// The data can be obtained fro 3 different places:
		// 1. The Data provider, assigned to the descriptor
		// 2. The descriptor itself
		// 3. The WaitForReadRequest object

		// First, let's check the data provider. We do it only when offset == 0 and then
		// save the value in the characteristic.
		final DataProvider dataProvider = dataProviders.get(descriptor);
		byte[] data = offset == 0 && dataProvider != null ? dataProvider.getData(device) : null;
		if (data != null) {
			// If the data were returned, store them for later use.
			// The client can request the data in multiple packets.
			assign(descriptor, data);
		} else {
			// If there was no provider or the data were null, or the offset is greater than 0,
			// get the value from the descriptor.
			data = descriptorValues == null || !descriptorValues.containsKey(descriptor)
					? descriptor.getValue()
					: descriptorValues.get(descriptor);
		}

		WaitForReadRequest waitForReadRequest = null;
		// Then, try to get the data from the WaitForReadRequest if the request awaits,
		if (awaitingRequest instanceof WaitForReadRequest
				// is registered for this descriptor
				&& awaitingRequest.descriptor == descriptor
				// and didn't have a trigger, or the trigger was started
				// (not necessarily completed)
				&& !awaitingRequest.isTriggerPending()) {
			waitForReadRequest = (WaitForReadRequest) awaitingRequest;
			// The data set in the WaitForReadRequest have priority over the data provider
			// and the value of the characteristic.
			waitForReadRequest.setDataIfNull(data);
			data = waitForReadRequest.getData(mtu);
		}

		// If data are longer than MTU - 1, cut the array.
		// Only ATT_MTU - 1 bytes can be sent in a single response.
		// If the data are longer, the client will request another read with an offset.
		if (data != null && data.length > mtu - 1) {
			data = Bytes.copy(data, offset, mtu - 1);
		}

		sendResponse(server, device, BluetoothGatt.GATT_SUCCESS, requestId, offset, data);

		if (waitForReadRequest != null) {
			waitForReadRequest.notifyPacketRead(device, data);

			// If the request is complete, start next one.
			if (!waitForReadRequest.hasMore() && (data == null || data.length < mtu - 1)) {
				waitForReadRequest.notifySuccess(device);
				awaitingRequest = null;
				nextRequest(true);
			}
		} else if (checkCondition()) {
			nextRequest(true);
		}
	}
Callback indicating the connection parameters were updated. Works on Android 8+.

@param gatt     GATT client involved.
@param interval Connection interval used on this connection, 1.25ms unit.
                Valid range is from 6 (7.5ms) to 3200 (4000ms).
@param latency  Slave latency for the connection in number of connection events.
                Valid range is from 0 to 499.
@param timeout  Supervision timeout for this connection, in 10ms unit.
                Valid range is from 10 (0.1s) to 3200 (32s)
@param status   {@link BluetoothGatt#GATT_SUCCESS} if the connection has been updated
                successfully.
ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java