PROBLEM
I am trying to get notification from more than one characteristic through BLE, I've seen some solution on the internet that I need to wait until the onDescriptorWrite() callback is finished (Which i think i have done here?), but I can't do onDescriptorWrite() for the second time for FILE_TX (code down there) notification. All of this is performed under onServicesDiscovery() - when I established BLE connection.
Is there anything that I am doing wrong here?
You can only have one outstanding gatt operation at a time. In this case you do two writeDescriptor calls before waiting until the first has completed. You must wait for https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int) until you can send the next one.
This is the best answer I can find but how can you tell that the onDescriptorWrite has been completed?
I tried putting Thread.sleep(500) in between for a work around but i doesn't work too.
Under onServicesDiscovery - gattCallback
for (gattCharacteristic in gattCharacteristics) {
uuid = gattCharacteristic.uuid
// // Log.d("GATT", "$uuid")
if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_READ_FILE_TX)) {
gatt.setCharacteristicNotification(gattCharacteristic, true)
val descriptorfile: BluetoothGattDescriptor = gattCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) ?: error("Required Client Characteristic Configuration not found")
descriptorfile.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
isSuccess = gatt.writeDescriptor(descriptorfile)
Log.d("tagfile", "FILE_TX Successful ? " + isSuccess)
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service File Tx characteristics")
}
else if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_TX)) {
gatt.setCharacteristicNotification(gattCharacteristic, true)
val descriptor: BluetoothGattDescriptor = gattCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) ?: error("Required Client Characteristic Configuration not found")
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
isSuccess = gatt.writeDescriptor(descriptor)
Log.d("tagfile", "TX Successful ? " + isSuccess)
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service Tx characteristics")
}
if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_RX)) {
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service Rx characteristics")
}
else if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_READ_FILE_RX)) {
gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
Log.d("tagfile", "Found Transparent service File Rx characteristics")
}
}
Under onDescriptorWrite - gattCallback
override fun onDescriptorWrite(
gatt: BluetoothGatt?,
descriptor: BluetoothGattDescriptor?,
status: Int
) {
Log.d("tagfile", "Status of gatt : " + status + " GATT FAILURE : " + BluetoothGatt.GATT_FAILURE)
}
RESULTS :
2020-01-24 09:41:51.359 8565-8587/com.example.ricco_ble D/tagfile: TX Successful ? true
2020-01-24 09:41:53.359 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service Tx characteristics
2020-01-24 09:41:53.360 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service Rx characteristics
2020-01-24 09:41:53.371 8565-8587/com.example.ricco_ble D/tagfile: FILE_TX Successful ? false
2020-01-24 09:41:53.371 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service File Tx characteristics
2020-01-24 09:41:53.372 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service File Rx characteristics
2020-01-24 09:41:53.424 8565-8587/com.example.ricco_ble D/tagfile: Status of gatt : 0 GATT FAILURE : 257
Instead of trying to build the system yourself, I recommend using the Android BLE Library from Nordic SemiConductor. I spent the better half of 4 months trying to do it myself. This library allows you to execute your calls using async with defined callbacks and not have to worry about issues like this.
I found it useful to follow two points when working with the Android BLE stack.
In any Gatt callbacks, only perform result copying, and schedule the next task to perform (not to execute). So that the callback function can be released as soon as possible.
Perform operation sequentially. Don't perform the next operation until you receive the callback from the previous operation.
From the code post above, you are nesting writing descriptors in serviceDiscovery callback. Also, you write the next descriptor before receiving the prior callback.
To have a more stable/predictable performance, you can refactor your code into something like
BluetoothGattCallback(){
onServiceDiscovery(gatt){
MainHandler.post((gatt)->setFirstNotification(gatt)) //do the scheduling, not direct execution here.
}
onDescriptorWrite(gatt){
if(the first descriptor write success) {
MainHandler.post((gatt)->setSecondNotification(gatt)) //do the scheduling
} else if (the second descriptor write success) {
MainHandler.post((gatt)->otherLogic(gatt)) //do the scheduling
}
}
}
fun setFirstNotification(gatt){
//your code to set descriptor value
}
fun setSecondNotification(gatt){
//your code to set descriptor value
}
fun otherLogic(gatt){
//your other code
}
This is roughly the idea how you would approach it if you want to build your communication app directly with the Android stack.
Related
I am using RSocket channel and trying to implement resume functionality, in case the connection to the server is lost. After trying different combinations of resume(), resumeSessionDuration(), resumeStreamTimeout() and resumeCleanupOnKeepAlive() /documentation: https://javadoc.io/static/io.rsocket/rsocket-core/1.0.0-RC3/io/rsocket/RSocketFactory.ServerRSocketFactory.html#resume--/ on the server and the mobile device, still nothing happens. On the mobile device I send data to the server each 10 seconds and when the connection is lost, the socket is disposed at the 3rd retry. Does anyone know what all these methods exactly do server- and client-side? There is almost no documentation at all...
This is the code on the server:
RSocketFactory.receive()
.resume()
.acceptor((setupPayload, reactiveSocket) -> Mono.just(responseHandler))
.transport(TcpServerTransport.create(buildSecuredTcpServer(address, port,
sslContext)))
.start()
.block();
The is the code on the mobile device:
socket = RSocketFactory.connect()
.errorConsumer(throwable -> {
if (throwable instanceof RejectedResumeException){
Log.d("tagg", "error: " + throwable.getMessage());
}
})
.resume()
.resumeStreamTimeout(Duration.ofSeconds(120))
.resumeSessionDuration(Duration.ofSeconds(600))
.keepAlive(Duration.ofSeconds(120), Duration.ofSeconds(120), 120)
.resumeStrategy(() -> new PeriodicResumeStrategy(Duration.ofSeconds(1)))
.metadataMimeType(BuildConfig.RSOCKET_METADATA_MIME_TYPE)
.dataMimeType(BuildConfig.RSOCKET_DATA_MIME_TYPE)
.transport(TcpClientTransport.create(tcpClient))
.start()
.block();
Objects.requireNonNull(socket)
.requestChannel(Flux.from(this::onSubscribe))
.doOnNext(this::receiveData)
.retryBackoff(Integer.MAX_VALUE, Duration.ofSeconds(1))
.subscribe();
I am trying to build a BLE Gatt Server with multiple custom services and multiple characteristics.
To begin with I used the Google Example: https://github.com/androidthings/sample-bluetooth-le-gattserver/tree/master/kotlin
This was straight forward and worked very well. I modified the UUIDs to fit mine and I could receive notifications and write to the chars with no problem.
This is where I define the services and chars:
fun createTimeService(): BluetoothGattService {
val service = BluetoothGattService(TIME_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
// Current Time characteristic
val currentTime = BluetoothGattCharacteristic(CURRENT_TIME,
//Read-only characteristic, supports notifications
BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ)
val configDescriptor = BluetoothGattDescriptor(CLIENT_CONFIG,
//Read/write descriptor
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
currentTime.addDescriptor(configDescriptor)
// Local Time Information characteristic
val localTime = BluetoothGattCharacteristic(LOCAL_TIME_INFO,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(currentTime)
service.addCharacteristic(localTime)
return service
}
fun createSerialService(): BluetoothGattService {
val service = BluetoothGattService(serialPortServiceID,
BluetoothGattService.SERVICE_TYPE_PRIMARY)
val serialData = BluetoothGattCharacteristic(serialDataCharacteristicID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE)
service.addCharacteristic(serialData)
return service
}
And here I am applying them to my server:
private fun startServer() {
bluetoothGattServer = bluetoothManager.openGattServer(this, gattServerCallback)
bluetoothGattServer?.addService(TimeProfile.createTimeService())
?: Log.w(TAG, "Unable to create GATT server")
bluetoothGattServer?.addService(TimeProfile.createSerialService())
?: Log.w(TAG, "Unable to create GATT server")
// Initialize the local UI
updateLocalUi(System.currentTimeMillis())
}
I would expect that everything would be working like before after adding the second service. But now if I try to write/subscribe to any of the characteristics (doesn't matter in which service) I just receive this:
W/BluetoothGattServer: onCharacteristicWriteRequest() no char for handle 42
W/BluetoothGattServer: onDescriptorWriteRequest() no desc for handle 43
I found what was going wrong. Apparently you cannot just add all services at once like I did. Adding the second service before the first one was confirmed lead to an Exception setting the services to null.
In the end I solved this by adding only one service initially.
Then in the onServiceAdded() Callback of the BleGattServerCallback() I started one after another.
We are currently trying to implement the transmission of images from a mobile device (in this case an IPhone) to a desktop application. We tried already the Bluetooth Serial plugin which works fine for Android but does not list any devices when scanning for our desktop application.
To cover iOS support (AFAIK iOS only supports BluetoothLE), we reimplemented our desktop application to use BluetoothLE and behave like a peripheral. Also we altered our Ionic application to use BLE plugin.
Now BluetoothLE only supports the transmission of packages with the size of 20 Byte whilst our image is about 500kb big. So we could obviously split our image into chunks and transmit it with the following function (taken from this gist):
function writeLargeData(buffer) {
console.log('writeLargeData', buffer.byteLength, 'bytes in',MAX_DATA_SEND_SIZE, 'byte chunks.');
var chunkCount = Math.ceil(buffer.byteLength / MAX_DATA_SEND_SIZE);
var chunkTotal = chunkCount;
var index = 0;
var startTime = new Date();
var transferComplete = function () {
console.log("Transfer Complete");
}
var sendChunk = function () {
if (!chunkCount) {
transferComplete();
return; // so we don't send an empty buffer
}
console.log('Sending data chunk', chunkCount + '.');
var chunk = buffer.slice(index, index + MAX_DATA_SEND_SIZE);
index += MAX_DATA_SEND_SIZE;
chunkCount--;
ble.write(
device_id,
service_uuid,
characteristic_uuid,
chunk,
sendChunk, // success callback - call sendChunk() (recursive)
function(reason) { // error callback
console.log('Write failed ' + reason);
}
)
}
// send the first chunk
sendChunk();
}
Still this would mean for us that we would have to launch about 25k transmissions which I assume will take a long time to complete. Now I wonder why is that the data transmission via Bluetooth is that handicapped.
If you want to try out L2CAP your could modify your Central desktop app somehow like this:
private let characteristicUUID = CBUUID(string: CBUUIDL2CAPPSMCharacteristicString)
...
Then advertize and publish a L2CAP channel:
let service = CBMutableService(type: peripheralUUID, primary: true)
let properties: CBCharacteristicProperties = [.read, .indicate]
let permissions: CBAttributePermissions = [.readable]
let characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: properties, value: nil, permissions: permissions)
self.characteristic = characteristic
service.characteristics = [characteristic]
self.manager.add(service)
self.manager.publishL2CAPChannel(withEncryption: false)
let data = [CBAdvertisementDataLocalNameKey : "Peripherial-42", CBAdvertisementDataServiceUUIDsKey: [peripheralUUID]] as [String : Any]
self.manager.startAdvertising(data)
In your
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
respective your
func peripheralManager(_ peripheral: CBPeripheralManager, didPublishL2CAPChannel PSM: CBL2CAPPSM, error: Error?) {
offer the PSM value (= kind of socket handle (UInt16), for Bluetooth stream connections):
let data = withUnsafeBytes(of: PSM) { Data($0) }
if let characteristic = self.characteristic {
characteristic.value = data
self.manager.updateValue(data, for: characteristic, onSubscribedCentrals: self.subscribedCentrals)
}
finally in
func peripheralManager(_ peripheral: CBPeripheralManager, didOpen channel: CBL2CAPChannel?, error: Error?)
open an input stream:
channel.inputStream.delegate = self
channel.inputStream.schedule(in: RunLoop.current, forMode: .default)
channel.inputStream.open()
where the delegate could look something like this:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasBytesAvailable:
if let stream = aStream as? InputStream {
...
//buffer is some UnsafeMutablePointer<UInt8>
let read = stream.read(buffer, maxLength: capacity)
print("\(read) bytes read")
}
case ...
}
iOS app with Central Role
Assuming you have something like that in your iOS code:
func sendImage(imageData: Data) {
self.manager = CBCentralManager(delegate: self, queue: nil)
self.imageData = imageData
self.bytesToWrite = imageData.count
NSLog("start")
}
then you can modify your peripheral on your iOS client to work with the L2Cap channel like this:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
...
if let characteristicValue = characteristic.value {
let psm = characteristicValue.withUnsafeBytes {
$0.load(as: UInt16.self)
}
print("using psm \(psm) for l2cap channel!")
peripheral.openL2CAPChannel(psm)
}
}
and as soon as you are notified of the opened channel, open the output stream on it:
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?)
...
channel.outputStream.delegate = self.streamDelegate
channel.outputStream.schedule(in: RunLoop.current, forMode: .default)
channel.outputStream.open()
Your supplied stream delegate might look like this:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasSpaceAvailable:
if let stream = aStream as? OutputStream, let imageData = self.imageData {
if self.bytesToWrite > 0 {
let bytesWritten = imageData.withUnsafeBytes {
stream.write(
$0.advanced(by: totalBytes),
maxLength: self.bytesToWrite
)
}
self.bytesToWrite -= bytesWritten
self.totalBytes += bytesWritten
print("\(bytesWritten) bytes written, \(bytesToWrite) remain")
} else {
NSLog("finished")
}
}
case ...
There is a cool WWDC video from 2017, What's New in Core Bluetooth, see here https://developer.apple.com/videos/play/wwdc2017/712/
At around 14:45 it starts to discuss how L2Cap channels are working.
At 28:47, the Get the Most out of Core Bluetooth topic starts, in which performance-related things are discussed in detail. That's probably exactly what you're interested in.
Finally, at 37:59 you will see various possible throughputs in kbps.
Based on the data shown on the slide, the maximum possible speed with L2CAP + EDL (Extended Data Length) + 15ms interval is 394 kbps.
Please have a look at this comment
The following snippet is taken from there
ble.requestMtu(yourDeviceId, 512, () => {
console.log('MTU Size ok.');
}, error => {
console.log('MTU Size failed.');
});
It is suggesting that you need to request the Mtu after connection and then I think you can break your message into chunks of 512 bytes rather than 20 bytes.
They have done this for android specific issue
First I should say that there are already tons of blog posts and Q&As on the exact same topic, so please read them first.
If you run iPhone 7, you have the LE Data Length Extension. The default MTU is also 185 bytes, which means you can send notifications or write without response commands with 182 bytes of payload. And please make sure you absolutely not use Write With Response or Indications since that will almost stall the transfer. When you run iOS in central mode you are restricted to 30 ms connection interval. Using a shorter connection interval can have benefits, so I would suggest you to run iOS in peripheral mode instead so you from the central side can set a connection interval of something short, say 12 ms. Since iPhone X and iPhone 8, you can also switch to the 2MBit/s PHY to get increased transfer speed. So to answer your actual question why BLE data transfer is handicapped: it's not, at least if you follow best practice.
You also haven't told anything about the system that runs your desktop application. If it supports 2 MBit/s PHY, LE Data Length Extension and a MTU of at least 185, then you should be happy and make sure your connections use all those features. If not, you should still get higher performance if you enable at least one of them.
I'm developing an application to pair with two kind of sensors with the app and also these sensors are pairing with each other, we developed our custom bluetooth communication protocol. The connection is working great usually, but there are still some errors that I'm having hard time to fix it.
Sensor 1 paring alone is working great, but every time I'm pairing both of them, then i close the app, pair it again with first sensor, I got disconnected with status 19 just after the connection is established, after I try again one or two times the connection will be established properly. I was thinking that was a problem with Gatt refresh, but I already tried one solution found here and I keep reproducing this error every time.
fun connectToDevice(device: BraincareDevice, pairColor: Int) {
BleLogHelper.writeLog("Connecting to ${device.name}")
isConnecting = true
val deviceType = if (device is Sensor) DeviceType.SENSOR else DeviceType.DONGLE
if (deviceType == DeviceType.SENSOR) {
sensorConnectionSubscription?.dispose()
} else {
dongleConnectionSubscription?.dispose()
}
val connectionSubscription = device.device.establishConnection(false)
.flatMapSingle { connection ->
if (device is Sensor) {
sensorConnection = connection
connectedSensor = device
} else if (device is Dongle) {
dongleConnection = connection
connectedDongle = device
}
connection.queue(CustomRefresh())
?.observeOn(Schedulers.io())
?.doOnComplete{
BleLogHelper.writeLog("GATT REFRESHED")
}
?.subscribe ({
BleLogHelper.writeLog("GATT REFRESHED")
},{
BleLogHelper.writeLog("FAIL REFRESHING GATT")
})
BleLogHelper.writeLog("Send Request Connection Command $deviceType")
val command = BraincareBluetoothCommandProtocol.createRequestConnectionCommandFrame(deviceType)
connection.writeCharacteristic(BraincareBluetoothProtocol.rxCharacteristicUUID, command)
}
.delay(300, TimeUnit.MILLISECONDS)
.subscribe({
BleLogHelper.writeLog("Connection Established ${device.type}")
val iscon= this.isConnecting
startBlinkingDeviceLed(deviceType, pairColor)
connectionFlowListeners.forEach { it.onConnectionEstablished(device) }
}, {
BleLogHelper.writeError("Connection Lost ${device.type}", it)
BleLogHelper.writeError("Retrying...", it)
val iscon= this.isConnecting
if (isMonitoring || isConnecting || deviceType == DeviceType.DONGLE){
connectionStateListeners.forEach {
if (deviceType == DeviceType.SENSOR) {
sensorNotificationSubscription?.dispose()
sensorRssiSubscription?.dispose()
blinkingDeviceLedsSubscription?.dispose()
disconnectFromDevice(DeviceType.SENSOR)
} else {
dongleRssiSubscription?.dispose()
disconnectFromDevice(DeviceType.DONGLE)
}
isConnecting = false
it.onConnectionLost(device)
}
}else{
reconnectToDevice(device, pairColor)
}
})
if (deviceType == DeviceType.SENSOR) {
sensorConnectionSubscription = connectionSubscription
} else {
dongleConnectionSubscription = connectionSubscription
}
}
The exception is firing just after connection.writeCharacteristic(BraincareBluetoothProtocol.rxCharacteristicUUID, command)
Log error:
2019-05-21 10:54:11.816 11797-11889/io.b4c.brain4care.debug E/BLEBC: 21/05/2019 10:54:11.810 - Connection Lost SENSOR
com.polidea.rxandroidble2.exceptions.BleDisconnectedException: Disconnected from D4:57:4F:53:44:E7 with status 19 (UNKNOWN)
at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback$2.onConnectionStateChange(RxBleGattCallback.java:77)
at android.bluetooth.BluetoothGatt$1$4.run(BluetoothGatt.java:268)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:789)
at android.bluetooth.BluetoothGatt.-wrap0(Unknown Source:0)
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:264)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:70)
at android.os.Binder.execTransact(Binder.java:682)
status=19 is GATT_CONN_TERMINATE_PEER_USER. This library since version 1.8.1 lists the reason properly. It is always advised to use the newest available version of basically any library as they bring improvements over time.
There is no clear question in the post. With the provided information it is not possible to tell more than the above — the peripheral you use disconnects from the app. Why this happens is up to your peripheral and you can possibly find an answer in the documentation.
Bear in mind that newer Android versions do not allow concurrent pairing procedures to more than one BLE device at a time. Pairing of two devices should be performed sequentially i.e.
Peripheral A starts pairing
Peripheral A finishes pairing
Peripheral B starts pairing
Peripheral B finishes pairing
Peripherals may be connected at the same time but only a single pairing procedure may be held at a time.
I'm trying to program Bluetooth low energy - AlertNotificationService (ANS). It is a bit weird service because typically services run on GAP peripheral. But ANS run on GAP central. So typical way of working shall be for example this:
Watch - GAP peripheral (broadcasting), GATT client
Phone - GAP central , GATT server
Basically it works for me but not always. And this instability is very strange to me. When I look with bluetooth LE analyzer I see that Android GATT server sometimes tells there are no characteristic in my profile...
It looks like this: "watch" ask for my GATT service (I know it is proprietary not ANS UUID)
Slave->Master ATT Rcvd Find By Type Value Request, GATT Primary Service Declaration 11:22:33:44:11:22:33:44:11:76:62:65:01:00:00:00
Phone says the service is there starting from handle 0x35
Master->Slave ATT Rcvd Find By Type Value Response Handle: 0x0035
Watch ask for charactersitisc from handle 0x35
Slave->Master ATT Rcvd Read By Type Request, GATT Characteristic Declaration, Handles: 0x0035..0xffff
But phone sometimes incorrectly says that there is no characteristic from that handle:
Master->Slave ATT Rcvd Error Response - Attribute Not Found, Handle: 0x0035
When I do add service and characteristic into GATT server I always get "true" from functions. I do it like this:
BluetoothGattService service =new BluetoothGattService(Vbe_AnsExt.UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characApp =
new BluetoothGattCharacteristic(Vbe_AnsExt.UUID_CharacApp,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic characMsg =
new BluetoothGattCharacteristic(Vbe_AnsExt.UUID_CharacMsg,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ );
boolean ret;
ret = service.addCharacteristic(characApp);
Log.i("vbeInit_ASN_Ext_Server","addCharApp retruned: "+ret);
ret = service.addCharacteristic(characMsg);
Log.i("vbeInit_ASN_Ext_Server","addCharMsg retruned: "+ret);
ret = mGattServer.addService(service);
Log.i("vbeInit_ASN_Ext_Server","addService retruned: "+ret);
Any idea what might be the problem? I noticed that there is function BluetoothGattServer::connect(). I'm not sure how to use it. I'm using standard BluetoothDevice::connectGatt(). But I guess if I would do something wrong it shall work never - not sometimes...
I'm using Android M (6.0.1) on Samsung SM-G920F.
[UPDATE]
I noticed that after phone restart it always works. After application close and reopen it usually does not works. It does not works in different ways ->
sometimes no characteristic is found;
sometimes only first characteristic is found
sometimes only last(second) characteristic is found.
When I start Gatt Server I do it like this:
mGattServer = bluetoothManager.openGattServer(appContext, mGattServerCallback);
mGattServer.clearServices();
When I close app (onDestroy()) I do close gattserver:
mGattServer.close();
I also tried not to close the GATT server, but it did not help. Any ideas what might get wrong between closing and reopening?
So I may have found the reason. I'm adding 2 services: ANS and my service ANS_extension. It seems it helps when I wait after adding the service on call back onServiceAdded()
So now it looks like this:
public static UUID UUID_SupportedNewAlertCategory = UUID.fromString("00002a47-0000-1000-8000-00805f9b34fb");
public static UUID UUID_NewAlert = UUID.fromString("00002a46-0000-1000-8000-00805f9b34fb");
// descriptor used for enabling notify feature
public static UUID UUID_Descr_new = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
mGattServer = bluetoothManager.openGattServer(appContext, mGattServerCallback);
mGattServer.clearServices();
waitServiceAdded = true;
BluetoothGattService service = new BluetoothGattService(Vbe_Ans.UUID_SERVICE,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characSupportedNewAlerCategory =
new BluetoothGattCharacteristic(Vbe_Ans.UUID_SupportedNewAlertCategory,
BluetoothGattCharacteristic.PROPERTY_READ ,
BluetoothGattCharacteristic.PERMISSION_READ);
BluetoothGattCharacteristic characNewAlert =
new BluetoothGattCharacteristic(Vbe_Ans.UUID_NewAlert,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ );
BluetoothGattDescriptor bgd = new BluetoothGattDescriptor(UUID_Descr_new,
BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
characNewAlert.addDescriptor(bgd);
...
ret = service.addCharacteristic(characSupportedNewAlerCategory);
ret = service.addCharacteristic(characNewAlert);
...
ret = mGattServer.addService(service);
while(waitServiceAdded);
waitServiceAdded = true;
//init second service similar way as previous
while(waitServiceAdded);
Then I clear waiting flag waitServiceAdded in GATT server call back. (I'm java newbie so you might want to use some thread mutex synchro to access boolean?)
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onServiceAdded (int status,
BluetoothGattService service){
waitServiceAdded = false;
}