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();
Related
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.
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've got an Android app I'm building with Unity that logs info on a simple python http server (hosted on a Digital Ocean Droplet). Here's my coroutine for poking the server:
IEnumerator pokeServer()
{
Debug.Log( "Establishing Server Connectivity..." );
using( var www = UnityWebRequest.Get( ServerURL ) )
{
Debug.Log( "Send Web Request" );
ServerStatus = ConnectionStatuses.AttemptingToConnect;
yield return www.SendWebRequest();
if( www.isNetworkError || www.isHttpError )
{
if( www.isNetworkError )
{
Debug.Log( "NETWORK ERROR: " + www );
}
else
{
Debug.Log( "HTTP ERROR: " + www );
}
ServerStatus = ConnectionStatuses.Unavailable;
}
else
{
Debug.Log( "Success! Server available!" );
ServerStatus = ConnectionStatuses.Connected;
}
}
}
If I run this on the Unity Editor, everything works fine. I can get a response from my server without issue. If I build and run this on an Android, the request is not sent to my server and I get no error message. The last line in the above code that's run is "yield return www.SendWebRequest();"
I've looked at the logcat, and there's no error. My server never gets any requests. However, if I poke "https://www.google.com," I do indeed get a response. This leads me to believe that this is some sort of http vs https issue, but I have no idea where to start. This code has been working for me for a very long time. Any advice would be very welcome!
I'd been using this phone from University of Pennsylvania's wireless network. It turns out that I can make an http request from a desktop but I can't do it from a phone. I took one of the devices home and tried it from my personal wifi and it all worked fine.
I'm doing a long write to a BLE for making an OTA update, but I need to wait for the write response of the BLE device for sending more data but I don't know how to catch the device write response, I'm using a Samsung galaxy tab s2 with android 7, and Kotlin for my code
override fun otaDataWrite(data:ByteArray) {
manager.connection?.flatMap { rxBleConnection: RxBleConnection? -> rxBleConnection?.createNewLongWriteBuilder()
?.setCharacteristicUuid(OTACharacteristics.OTA_DATA.uuid)
?.setBytes(data)
?.setMaxBatchSize(totalPackages)
?.build()
}?.subscribe({ t: ByteArray? ->
Log.i("arrive", "data ${converter.bytesToHex(t)}")
manageOtaWrite()
}, { t: Throwable? -> t?.printStackTrace() })
every time that I write the characteristic the subscriptions respond me immediately with the written data, I need capture the response of the characteristic, for sending more data
You are writing about response from the characteristic — I assume that the characteristic you refer is the one with UUID=OTA_DATA. The Long Write consist of small writes internally (so called batches).
What you probably want to achieve is something like:
fun otaDataWrite(data: ByteArray) {
manager.connection!!.setupNotification(OTA_DATA) // first we need to get the notification on to get the response
.flatMap { responseNotificationObservable -> // when the notification is ready we create the long write
connection.createNewLongWriteBuilder()
.setCharacteristicUuid(OTA_DATA)
.setBytes(data)
// .setMaxBatchSize() // -> if omitted will default to the MTU (20 bytes if MTU was not changed). Should be used only if a single write should be less than MTU in size
.setWriteOperationAckStrategy { writeCompletedObservable -> // we need to postpone writing of the next batch of data till we get the response notification
Observable.zip( // so we zip the response notification
responseNotificationObservable,
writeCompletedObservable, // with the acknowledgement of the written batch
{ _, writeCompletedBoolean -> writeCompletedBoolean } // when both are available the next batch will be written
)
}
.build()
}
.take(1) // with this line the notification that was set above will be discarded after the long write will finish
.subscribe(
{ byteArray ->
Log.i("arrive", "data ${converter.bytesToHex(byteArray)}")
manageOtaWrite()
},
{ it.printStackTrace() }
)
}
Well, after a lot of testing, I finally develop a standalone class for the OTA update with the android BLE API, and I used it together with all my RxBle methods, I don't know if I have a hardware problem or something else, but I solve the problem, thanks a lot.
I'm communication with a server through a tcp socket connection, i'm able to read lines that ends with \n fine, however when the line is not terminated (ends in \n) i'm not able to read it. I tried the following but it didn't work and caused my app to freeze at startup:
private Socket socket;
private BufferedReader input;
public boolean isConnected;
#Override
public void onCreate()
{
try
{
socket = new Socket ("server.ip.add.ress", 23456);
input = new BufferedReader (new InputStreamReader (socket.getInputStream());
handshake();
isConnected = true;
}
catch // Handle IOException and UnknownHostException
}
// custom runnable to read availabe input from the server
private class MyRunnable implements Runnable
{
private volativle String value;
public String getValue()
{
return value;
}
#Override
public void run()
{
int count;
char[] buffer = new char[10]; // expected message 'username: '
try
{
count = input.read (buffer, 0, 10);
if (count > 0) value = new String (buffer);
}
catch // IOException
}
}
// when connection is established with server expect 'username: ' from
// the server and send the user name back to it
public void handshake()
{
MyRunnable runnable = new MyRunnable();
try
{
Thread thread = new Thread (runnable);
thread.start();
thread.join();
String greeting = runnable.getValue();
if (greeting.equals ("username: ")) // Send username back
}
catch // InterruptedException
}
why is it hanging? and how can i read a non terminated line?
Edit:
To clarify: The server sends the greeting message username: immediately after the connection is established with a client, the client wait for the greeting and send back it's username when received (that's what handshake() does), if no handshake the client disconnects otherwise it start listening for incoming messages. Because i need to know if handshake is complete before starting the listener i had to use Thread.join().
The problem: Thanks for the comments and answers below, it turned out that BufferedReader.read() blocks the thread and waits until something is sent from the server and if nothing is being sent it causes the app to hang, Therefor there's no way to find out if the line has ended.
The solution: In my specific situation i just wanted to know if a specific message is sent "username: " so i used read (buffer, 0, 10) to read exactly 10 characters (the length of "username: "), and because it blocks if nothing is sent i used Thread.join (1000) which waits only one second and then if nothing received i disconnect the client.
Why is it hanging?
This is what it is suppose to be. It will block the thread if no data is available to read. This is also why you want to put it in a background thread.
Can it not just return if nothing is available?
What you are looking for is ready(), which will tell you whether there is available data or not.
Indicates whether this reader is ready to be read without blocking.
Returns
true if this reader will not block when read is called, false if unknown or blocking will occur.
But you should be very careful when using this function. Because networking is a lot about timing. The fact that you don't have any data to read at this second doesn't necessary mean that it won't be any data in the next second.
So a better design of the server should be more or less as the following:
If the username is found, return the username
If the username is not found, return an error message to let the client side know that the username is not found
There's no need for the thread. Your goal is to wait until you've read what you've been waiting for. Why not just let read() perform the wait for you?
What you're struggling with is the classic problem of TCP communication: "when do I know that I've got everything the server sent?"
In your case, you're expecting to read bytes until the collection of bytes ends with "username: ". So, change your algorithm to perform 1 byte reads (filling a buffer as you go) until that buffer ends with "username: ".
You can make a more complicated algorithm -- which would be more efficient -- that would attempt to read multiple bytes at a time and append them to a buffer -- performing your check each time. But either strategy is logically equivalent.
I also recommend just using the InputStreamReader. It has various read() methods. I am a bit suspicious about the BufferedInputReader, especially when dealing with data that isn't newline terminated. I'm probably just paranoid. I've just never used it when writing TCP client/server programs, so I'm not sure.