1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-08-25 08:40:43 +02:00

Xiaomi: refactor XiaomiCharacteristic to use callback per message

This commit is contained in:
MrYoranimo 2024-01-03 17:46:57 +01:00 committed by José Rebelo
parent 592a52054f
commit e5c2bd51c2
2 changed files with 83 additions and 60 deletions

View File

@ -70,8 +70,6 @@ public class XiaomiCharacteristic {
private XiaomiChannelHandler channelHandler = null;
private SendCallback callback;
public XiaomiCharacteristic(final XiaomiBleSupport support,
final BluetoothGattCharacteristic bluetoothGattCharacteristic,
@Nullable final XiaomiAuthService authService) {
@ -90,10 +88,6 @@ public class XiaomiCharacteristic {
this.channelHandler = handler;
}
public void setCallback(final SendCallback callback) {
this.callback = callback;
}
public void setEncrypted(final boolean encrypted) {
this.isEncrypted = encrypted;
}
@ -113,11 +107,29 @@ public class XiaomiCharacteristic {
this.currentPayload = null;
}
/**
* Write bytes to this characteristic, encrypting and splitting it into chunks if necessary.
* Callback will be notified when a (n)ack has been received by the remote device.
*/
public void write(final String taskName, final byte[] value, final SendCallback callback) {
write(null, new Payload(taskName, value, callback));
}
/**
* Write bytes to this characteristic, encrypting and splitting it into chunks if necessary.
*/
public void write(final String taskName, final byte[] value) {
write(null, new Payload(taskName, value));
write(taskName, value, null);
}
/**
* Write bytes to this characteristic, encrypting and splitting it into chunks if necessary. Uses
* the provided builder if we need to schedule something, otherwise it will be queued as other
* commands. The callback will be notified when a (n)ack has been received from the remote
* device in response to the payload being sent.
*/
public void write(final TransactionBuilder builder, final byte[] value, final SendCallback callback) {
write(builder, new Payload(builder.getTaskName(), value, callback));
}
/**
@ -125,7 +137,7 @@ public class XiaomiCharacteristic {
* the provided if we need to schedule something, otherwise it will be queued as other commands.
*/
public void write(final TransactionBuilder builder, final byte[] value) {
write(builder, new Payload(builder.getTaskName(), value));
write(builder, value, null);
}
private void write(final TransactionBuilder builder, final Payload payload) {
@ -134,17 +146,6 @@ public class XiaomiCharacteristic {
}
public void onCharacteristicChanged(final byte[] value) {
if (Arrays.equals(value, PAYLOAD_ACK)) {
LOG.debug("Got ack");
currentPayload = null;
waitingAck = false;
if (callback != null) {
callback.onSend(payloadQueue.size());
}
sendNext(null);
return;
}
final ByteBuffer buf = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);
final int chunk = buf.getShort();
@ -201,11 +202,11 @@ public class XiaomiCharacteristic {
switch (subtype) {
case 0:
LOG.debug("Got chunked ack end");
if (currentPayload != null && currentPayload.getCallback() != null) {
currentPayload.getCallback().onSend();
}
currentPayload = null;
sendingChunked = false;
if (callback != null) {
callback.onSend(payloadQueue.size());
}
sendNext(null);
return;
case 1:
@ -226,17 +227,16 @@ public class XiaomiCharacteristic {
return;
case 2:
LOG.warn("Got chunked nack for {}", currentPayload.getTaskName());
if (currentPayload != null && currentPayload.getCallback() != null) {
currentPayload.getCallback().onNack();
}
currentPayload = null;
sendingChunked = false;
if (callback != null) {
callback.onSend(payloadQueue.size());
}
sendNext(null);
return;
}
LOG.warn("Unknown chunked ack subtype {} for {}", subtype, currentPayload.getTaskName());
return;
case 2:
// Single command
@ -261,8 +261,28 @@ public class XiaomiCharacteristic {
return;
case 3:
// ack
LOG.debug("Got ack");
final byte result = buf.get();
if (result == 0) {
LOG.debug("Got ack for {}", currentPayload.getTaskName());
if (currentPayload != null && currentPayload.getCallback() != null) {
currentPayload.getCallback().onSend();
}
} else {
LOG.warn("Got single cmd NACK ({}) for {}", result, currentPayload.getTaskName());
if (currentPayload != null && currentPayload.getCallback() != null) {
currentPayload.getCallback().onNack();
}
}
currentPayload = null;
waitingAck = false;
sendNext(null);
return;
}
LOG.warn("Unhandled command type {}", type);
}
}
@ -375,10 +395,16 @@ public class XiaomiCharacteristic {
// Bytes that will actually be sent (might be encrypted)
private byte[] bytesToSend;
private final SendCallback callback;
public Payload(final String taskName, final byte[] bytes) {
public Payload(final String taskName, final byte[] bytes, final SendCallback callback) {
this.taskName = taskName;
this.bytes = bytes;
this.callback = callback;
}
public Payload(final String taskName, final byte[] bytes) {
this(taskName, bytes, null);
}
public String getTaskName() {
@ -392,9 +418,11 @@ public class XiaomiCharacteristic {
public byte[] getBytesToSend() {
return bytesToSend != null ? bytesToSend : bytes;
}
public SendCallback getCallback() { return this.callback; }
}
public interface SendCallback {
void onSend(int remaining);
void onSend();
void onNack();
}
}

View File

@ -65,8 +65,7 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
if (dataUploadAck.getUnknown2() != 0 || dataUploadAck.getResumePosition() != 0) {
LOG.warn("Unexpected response");
this.currentType = 0;
this.currentBytes = null;
onUploadFinish(false);
return;
}
@ -76,6 +75,7 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
chunkSize = 2048;
}
LOG.debug("Using chunk size of {} bytes", chunkSize);
doUpload(currentType, currentBytes);
return;
}
@ -143,38 +143,35 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
final int partSize = chunkSize - 4; // 2 + 2 at beginning of each for total and progress
final int totalParts = (int) Math.ceil(payload.length / (float) partSize);
characteristic.setCallback(remainingParts -> {
final int totalBytes = totalParts * 4 + payload.length;
int progressBytes = totalParts * 4 + payload.length;
if (remainingParts > 1) {
progressBytes -= (remainingParts - 1) * partSize;
}
if (remainingParts > 0) {
progressBytes -= (payload.length % partSize);
}
final int progressPercent = Math.round((100.0f * progressBytes) / totalBytes);
LOG.debug("Data upload progress: {} parts remaining ({}%)", remainingParts, progressPercent);
if (remainingParts > 0) {
if (callback != null) {
callback.onUploadProgress(progressPercent);
}
} else {
onUploadFinish(true);
}
});
for (int i = 0; i * partSize < payload.length; i++) {
final int currentPart = i + 1;
final int startIndex = i * partSize;
final int endIndex = Math.min((i + 1) * partSize, payload.length);
LOG.debug("Uploading part {} of {}, from {} to {}", (i + 1), totalParts, startIndex, endIndex);
final int endIndex = Math.min(currentPart * partSize, payload.length);
LOG.debug("Uploading part {} of {}, from {} to {}", currentPart, totalParts, startIndex, endIndex);
final byte[] chunkToSend = new byte[4 + endIndex - startIndex];
BLETypeConversions.writeUint16(chunkToSend, 0, totalParts);
BLETypeConversions.writeUint16(chunkToSend, 2, i + 1);
BLETypeConversions.writeUint16(chunkToSend, 2, currentPart);
System.arraycopy(payload, startIndex, chunkToSend, 4, endIndex - startIndex);
characteristic.write("upload part " + (i + 1) + " of " + totalParts, chunkToSend);
characteristic.write("upload part " + currentPart + " of " + totalParts, chunkToSend, new XiaomiCharacteristic.SendCallback() {
@Override
public void onSend() {
final int progressPercent = Math.round((100.0f * currentPart) / totalParts);
LOG.debug("Data upload progress: {}/{} parts sent ({}% done)", currentPart, totalParts, progressPercent);
if (currentPart >= totalParts) {
onUploadFinish(true);
} else if (callback != null) {
callback.onUploadProgress(progressPercent);
}
}
@Override
public void onNack() {
LOG.warn("NACK received while uploading part {}/{}", currentPart, totalParts);
// TODO callback.onUploadFinish(false); ?
}
});
}
}
@ -189,8 +186,6 @@ public class XiaomiDataUploadService extends AbstractXiaomiService {
if (callback != null) {
callback.onUploadFinish(success);
}
characteristic.setCallback(null);
}
public interface Callback {