1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-01 07:02:57 +01:00

Huami 2021: Handle chunked ACKs

This commit is contained in:
José Rebelo 2023-06-11 15:45:41 +01:00 committed by José Rebelo
parent 4e9d883ae2
commit 74dac3f5cd
3 changed files with 71 additions and 10 deletions

View File

@ -33,6 +33,10 @@ public class Huami2021ChunkedDecoder {
private int currentLength; private int currentLength;
ByteBuffer reassemblyBuffer; ByteBuffer reassemblyBuffer;
// Keep track of last handle and count for acks
private byte lastHandle;
private byte lastCount;
private volatile byte[] sharedSessionKey; private volatile byte[] sharedSessionKey;
private Huami2021Handler huami2021Handler; private Huami2021Handler huami2021Handler;
@ -52,16 +56,25 @@ public class Huami2021ChunkedDecoder {
this.huami2021Handler = huami2021Handler; this.huami2021Handler = huami2021Handler;
} }
public void decode(final byte[] data) { public byte getLastHandle() {
return lastHandle;
}
public byte getLastCount() {
return lastCount;
}
public boolean decode(final byte[] data) {
int i = 0; int i = 0;
if (data[i++] != 0x03) { if (data[i++] != 0x03) {
//LOG.warn("Ignoring non-chunked payload"); LOG.warn("Ignoring non-chunked payload");
return; return false;
} }
final byte flags = data[i++]; final byte flags = data[i++];
final boolean encrypted = ((flags & 0x08) == 0x08); final boolean encrypted = ((flags & 0x08) == 0x08);
final boolean firstChunk = ((flags & 0x01) == 0x01); final boolean firstChunk = ((flags & 0x01) == 0x01);
final boolean lastChunk = ((flags & 0x02) == 0x02); final boolean lastChunk = ((flags & 0x02) == 0x02);
final boolean needsAck = ((flags & 0x04) == 0x04);
if (force2021Protocol) { if (force2021Protocol) {
i++; // skip extended header i++; // skip extended header
@ -69,9 +82,10 @@ public class Huami2021ChunkedDecoder {
final byte handle = data[i++]; final byte handle = data[i++];
if (currentHandle != null && currentHandle != handle) { if (currentHandle != null && currentHandle != handle) {
LOG.warn("ignoring handle {}, expected {}", handle, currentHandle); LOG.warn("ignoring handle {}, expected {}", handle, currentHandle);
return; return false;
} }
byte count = data[i++]; lastHandle = handle;
lastCount = data[i++];
if (firstChunk) { // beginning if (firstChunk) { // beginning
int full_length = (data[i++] & 0xff) | ((data[i++] & 0xff) << 8) | ((data[i++] & 0xff) << 16) | ((data[i++] & 0xff) << 24); int full_length = (data[i++] & 0xff) | ((data[i++] & 0xff) << 8) | ((data[i++] & 0xff) << 16) | ((data[i++] & 0xff) << 24);
currentLength = full_length; currentLength = full_length;
@ -96,7 +110,7 @@ public class Huami2021ChunkedDecoder {
LOG.warn("Got encrypted message, but there's no shared session key"); LOG.warn("Got encrypted message, but there's no shared session key");
currentHandle = null; currentHandle = null;
currentType = 0; currentType = 0;
return; return false;
} }
byte[] messagekey = new byte[16]; byte[] messagekey = new byte[16];
@ -110,7 +124,7 @@ public class Huami2021ChunkedDecoder {
LOG.warn("error decrypting " + e); LOG.warn("error decrypting " + e);
currentHandle = null; currentHandle = null;
currentType = 0; currentType = 0;
return; return false;
} }
} }
LOG.debug( LOG.debug(
@ -128,5 +142,7 @@ public class Huami2021ChunkedDecoder {
currentHandle = null; currentHandle = null;
currentType = 0; currentType = 0;
} }
return needsAck;
} }
} }

View File

@ -2293,8 +2293,8 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
} else if (HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION.equals(characteristicUUID)) { } else if (HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION.equals(characteristicUUID)) {
handleConfigurationInfo(characteristic.getValue()); handleConfigurationInfo(characteristic.getValue());
return true; return true;
} else if (HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ.equals(characteristicUUID) && huami2021ChunkedDecoder != null) { } else if (HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER_2021_READ.equals(characteristicUUID)) {
huami2021ChunkedDecoder.decode(characteristic.getValue()); handleChunked(characteristic.getValue());
return true; return true;
} else if (HuamiService.UUID_CHARACTERISTIC_RAW_SENSOR_DATA.equals(characteristicUUID)) { } else if (HuamiService.UUID_CHARACTERISTIC_RAW_SENSOR_DATA.equals(characteristicUUID)) {
handleRawSensorData(characteristic.getValue()); handleRawSensorData(characteristic.getValue());
@ -2455,6 +2455,47 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport implements
} }
} }
private void handleChunked(final byte[] value) {
switch (value[0]) {
case 0x03:
if (huami2021ChunkedDecoder != null) {
final boolean needsAck = huami2021ChunkedDecoder.decode(value);
if (needsAck) {
sendChunkedAck();
}
} else {
LOG.warn("Got chunked payload, but decoder is null");
}
return;
case 0x04:
final byte handle = value[2];
final byte count = value[4];
LOG.info("Got chunked ack, handle={}, count={}", handle, count);
// TODO: We should probably update the handle and count on the encoder
return;
default:
LOG.warn("Unhandled chunked payload of type {}", value[0]);
}
}
public void sendChunkedAck() {
if (characteristicChunked2021Read == null) {
LOG.error("Chunked read characteristic is null, can't send ack");
return;
}
final byte handle = huami2021ChunkedDecoder.getLastHandle();
final byte count = huami2021ChunkedDecoder.getLastCount();
try {
final TransactionBuilder builder = performInitialized("send chunked ack");
builder.write(characteristicChunked2021Read, new byte[] {0x04, 0x00, handle, 0x01, count});
builder.queue(getQueue());
} catch (final Exception e) {
LOG.error("Failed to send chunked ack", e);
}
}
private void decodeAndUpdateAlarmStatus(byte[] response, boolean withTimes) { private void decodeAndUpdateAlarmStatus(byte[] response, boolean withTimes) {
List<nodomain.freeyourgadget.gadgetbridge.entities.Alarm> alarms = DBHelper.getAlarms(gbDevice); List<nodomain.freeyourgadget.gadgetbridge.entities.Alarm> alarms = DBHelper.getAlarms(gbDevice);
int maxAlarms = 10; int maxAlarms = 10;

View File

@ -127,7 +127,11 @@ public class InitOperation2021 extends InitOperation implements Huami2021Handler
return super.onCharacteristicChanged(gatt, characteristic); return super.onCharacteristicChanged(gatt, characteristic);
} }
this.huami2021ChunkedDecoder.decode(value); final boolean needsAck = huami2021ChunkedDecoder.decode(value);
if (needsAck) {
huamiSupport.sendChunkedAck();
}
return true; return true;
} }