1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-03 02:06:21 +02:00

Bangle.js flow control. If the Bangle is busy and unable to accept more data, Gadgetbridge will now pause sending until the Bangle is ready for more.

This works by adding the ability to pause the sending of data from the Bluetooth LE queue. While BtLEQueue is modified, unless setPaused(true) is called it behaves exactly as before so shouldn't cause any issues.
This commit is contained in:
Gordon Williams 2022-08-19 09:25:18 +01:00 committed by Gitea
parent bb1323dd61
commit 0dd0b2bead
2 changed files with 43 additions and 2 deletions

View File

@ -51,6 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction;
/**
* One queue/thread per connectable device.
@ -70,6 +71,7 @@ public final class BtLEQueue {
private volatile boolean mCrashed;
private volatile boolean mAbortTransaction;
private volatile boolean mAbortServerTransaction;
private volatile boolean mPauseTransaction = false;
private final Context mContext;
private CountDownLatch mWaitForActionResultLatch;
@ -117,7 +119,7 @@ public final class BtLEQueue {
break;
}
if (LOG.isDebugEnabled()) {
LOG.debug("About to run action: " + action);
LOG.debug("About to run server action: " + action);
}
if (action.run(mBluetoothGattServer)) {
// check again, maybe due to some condition, action did not need to write, so we can't wait
@ -149,6 +151,15 @@ public final class BtLEQueue {
LOG.info("Aborting running transaction");
break;
}
while ((action instanceof WriteAction) && mPauseTransaction && !mAbortTransaction) {
LOG.info("Pausing WriteAction");
try {
Thread.sleep(100);
} catch (Exception e) {
LOG.info("Exception during pause: "+e.toString());
break;
}
}
mWaitCharacteristic = action.getCharacteristic();
mWaitForActionResultLatch = new CountDownLatch(1);
if (LOG.isDebugEnabled()) {
@ -222,6 +233,7 @@ public final class BtLEQueue {
* @return <code>true</code> whether the connection attempt was successfully triggered and <code>false</code> if that failed or if there is already a connection
*/
public boolean connect() {
mPauseTransaction = false;
if (isConnected()) {
LOG.warn("Ingoring connect() because already connected.");
return false;
@ -286,6 +298,7 @@ public final class BtLEQueue {
gatt.close();
setDeviceConnectionState(State.NOT_CONNECTED);
}
mPauseTransaction = false;
BluetoothGattServer gattServer = mBluetoothGattServer;
if (gattServer != null) {
mBluetoothGattServer = null;
@ -299,6 +312,7 @@ public final class BtLEQueue {
LOG.debug("handleDisconnected: " + status);
internalGattCallback.reset();
mTransactions.clear();
mPauseTransaction = false;
mAbortTransaction = true;
mAbortServerTransaction = true;
if (mWaitForActionResultLatch != null) {
@ -332,6 +346,7 @@ public final class BtLEQueue {
if (mAutoReconnect && mBluetoothGatt != null) {
LOG.info("Enabling automatic ble reconnect...");
boolean result = mBluetoothGatt.connect();
mPauseTransaction = false;
if (result) {
setDeviceConnectionState(State.WAITING_FOR_RECONNECT);
}
@ -340,6 +355,10 @@ public final class BtLEQueue {
return false;
}
public void setPaused(boolean paused) {
mPauseTransaction = paused;
}
public void dispose() {
if (mDisposed) {
return;

View File

@ -792,8 +792,30 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
// check to see if we get more data - if so, increase out MTU for sending
if (allowHighMTU && chars.length > mtuSize)
mtuSize = chars.length;
// Scan for flow control characters
for (int i=0;i<chars.length;i++) {
boolean ignoreChar = false;
if (chars[i]==19 /* XOFF */) {
getQueue().setPaused(true);
LOG.info("RX: XOFF");
ignoreChar = true;
}
if (chars[i]==17 /* XON */) {
getQueue().setPaused(false);
LOG.info("RX: XON");
ignoreChar = true;
}
if (ignoreChar) {
// remove char from the array. Generally only one XON/XOFF per stream so creating a new array each time is fine
byte[] c = new byte[chars.length - 1];
System.arraycopy(chars, 0, c, 0, i); // copy before
System.arraycopy(chars, i+1, c, i, chars.length - i - 1); // copy after
chars = c;
i--; // back up one (because we deleted it)
}
}
String packetStr = new String(chars);
LOG.info("RX: " + packetStr);
LOG.debug("RX: " + packetStr);
// logging
addReceiveHistory(packetStr);
// split into input lines