1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-24 10:56:50 +01:00

Huawei: Quick fix issue with concatenated packets.

This commit is contained in:
Me7c7 2024-09-05 11:55:32 +03:00 committed by José Rebelo
parent cde10a6dce
commit 1f5e9b52b8
5 changed files with 142 additions and 94 deletions

View File

@ -235,7 +235,7 @@ public class HuaweiPacket {
}
}
protected static final int PACKET_MINIMAL_SIZE = 6;
protected static final int PACKET_MINIMAL_SIZE = 3; // magic + data size
protected ParamsProvider paramsProvider;
@ -288,6 +288,7 @@ public class HuaweiPacket {
this.partialPacket = packet.partialPacket;
this.payload = packet.payload;
this.complete = packet.complete;
this.left = packet.left;
if (packet.isEncrypted)
this.isEncrypted = true;
@ -303,7 +304,14 @@ public class HuaweiPacket {
*/
public void parseTlv() throws ParseException {}
private int left = 0;
public int getLeft() {
return this.left;
}
private void parseData(byte[] data) throws ParseException {
this.left = 0;
if (partialPacket != null) {
int newCapacity = partialPacket.length + data.length;
data = ByteBuffer.allocate(newCapacity)
@ -313,23 +321,13 @@ public class HuaweiPacket {
}
ByteBuffer buffer = ByteBuffer.wrap(data);
if (buffer.capacity() < PACKET_MINIMAL_SIZE) {
if (buffer.capacity() < 1) {
throw new LengthMismatchException("Packet length mismatch : "
+ buffer.capacity()
+ " != 6");
+ " < 1");
}
byte magic = buffer.get();
short expectedSize = buffer.getShort();
int isSliced = buffer.get();
if (isSliced == 1 || isSliced == 2 || isSliced == 3) {
buffer.get(); // Throw away slice flag
}
byte[] newPayload = new byte[buffer.remaining() - 2];
buffer.get(newPayload, 0, buffer.remaining() - 2);
short expectedChecksum = buffer.getShort();
buffer.rewind();
if (magic != HUAWEI_MAGIC) {
throw new MagicMismatchException("Magic mismatch : "
@ -337,26 +335,40 @@ public class HuaweiPacket {
+ " != 0x5A");
}
int newPayloadLen = newPayload.length + 1;
if (isSliced == 1 || isSliced == 2 || isSliced == 3) {
newPayloadLen = newPayload.length + 2;
if (buffer.capacity() < PACKET_MINIMAL_SIZE) {
this.partialPacket = data;
return;
}
if (expectedSize != (short) newPayloadLen) {
if (expectedSize > (short) newPayloadLen) {
// Older band and BT version do not handle message with more than 256 bits.
this.partialPacket = data;
return;
} else {
throw new LengthMismatchException("Expected length mismatch : "
+ expectedSize
+ " < "
+ (short) newPayloadLen);
}
short expectedSize = buffer.getShort();
if(expectedSize < 0) {
throw new LengthMismatchException("Expected length mismatch : " + expectedSize);
}
if (expectedSize + 2 > buffer.remaining()) {
// Older band and BT version do not handle message with more than 256 bits.
this.partialPacket = data;
return;
}
this.partialPacket = null;
byte[] dataNoCRC = new byte[buffer.capacity() - 2];
buffer.get(dataNoCRC, 0, buffer.capacity() - 2);
int addLen = 1;
int isSliced = buffer.get();
if (isSliced == 1 || isSliced == 2 || isSliced == 3) {
buffer.get(); // Throw away slice flag
addLen++;
}
byte[] newPayload = new byte[expectedSize - addLen];
buffer.get(newPayload, 0, expectedSize - addLen);
short expectedChecksum = buffer.getShort();
this.left = buffer.remaining();
buffer.rewind();
byte[] dataNoCRC = new byte[expectedSize + 3];
buffer.get(dataNoCRC, 0, expectedSize + 3);
short actualChecksum = (short) CheckSums.getCRC16(dataNoCRC, 0x0000);
if (actualChecksum != expectedChecksum) {
throw new ChecksumIncorrectException("Checksum mismatch : "
@ -388,7 +400,8 @@ public class HuaweiPacket {
if (
(serviceId == 0x0a && commandId == 0x05) ||
(serviceId == 0x28 && commandId == 0x06) ||
(serviceId == 0x2c && commandId == 0x05)
(serviceId == 0x2c && commandId == 0x05) ||
(serviceId == 0x1c && commandId == 0x05)
) {
// TODO: this doesn't seem to be TLV
this.payload = newPayload;

View File

@ -16,6 +16,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants.HUAWEI_MAGIC;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;
import android.content.SharedPreferences;
@ -934,23 +936,7 @@ public class HuaweiSupportProvider {
}
public void onSocketRead(byte[] data) {
// The data can contain multiple packets, which need to be split.
// But we also need to take into account partial packets (where data does not contain a full packet)
if (data[0] != 0x5a) {
// Part of partial packet, just parse
responseManager.handleData(data);
return;
}
ByteBuffer bData = ByteBuffer.wrap(data);
while (bData.remaining() != 0) {
int dataLen = bData.getShort(bData.position() + 1) + 0x05;
if (dataLen > bData.remaining())
dataLen = bData.remaining(); // Part of partial packet, just parse the remainder
byte[] newData = new byte[dataLen];
bData.get(newData, 0, dataLen);
responseManager.handleData(newData);
}
}
public void removeInProgressRequests(Request req) {

View File

@ -20,6 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -80,47 +81,57 @@ public class ResponseManager {
* @param data The received data
*/
public void handleData(byte[] data) {
try {
if (receivedPacket == null)
receivedPacket = new HuaweiPacket(support.getParamsProvider()).parse(data);
else
receivedPacket = receivedPacket.parse(data);
} catch (HuaweiPacket.ParseException e) {
LOG.error("Packet parse exception", e);
//NOTE: This is a quick fix issue with concatenated packets.
//TODO: Extract transport related code from packet.
int left = 0;
do {
if(left > 0)
data = Arrays.copyOfRange(data, data.length - left, data.length);
// Clean up so the next message may be parsed correctly
this.receivedPacket = null;
return;
}
try {
if (receivedPacket == null)
receivedPacket = new HuaweiPacket(support.getParamsProvider()).parse(data);
else
receivedPacket = receivedPacket.parse(data);
if (receivedPacket.complete) {
Request handler = null;
synchronized (handlers) {
for (Request req : handlers) {
if (req.handleResponse(receivedPacket)) {
handler = req;
break;
}
}
left = receivedPacket.getLeft();
} catch (HuaweiPacket.ParseException e) {
LOG.error("Packet parse exception", e);
// Clean up so the next message may be parsed correctly
this.receivedPacket = null;
return;
}
if (handler == null) {
LOG.debug("Service: " + Integer.toHexString(receivedPacket.serviceId & 0xff) + ", command: " + Integer.toHexString(receivedPacket.commandId & 0xff) + ", asynchronous response.");
// Asynchronous response
asynchronousResponse.handleResponse(receivedPacket);
} else {
LOG.debug("Service: " + Integer.toHexString(receivedPacket.serviceId & 0xff) + ", command: " + Integer.toHexString(receivedPacket.commandId & 0xff) + ", handled by: " + handler.getClass());
if (handler.autoRemoveFromResponseHandler()) {
synchronized (handlers) {
handlers.remove(handler);
if (receivedPacket.complete) {
Request handler = null;
synchronized (handlers) {
for (Request req : handlers) {
if (req.handleResponse(receivedPacket)) {
handler = req;
break;
}
}
}
handler.handleResponse();
if (handler == null) {
LOG.debug("Service: " + Integer.toHexString(receivedPacket.serviceId & 0xff) + ", command: " + Integer.toHexString(receivedPacket.commandId & 0xff) + ", asynchronous response.");
// Asynchronous response
asynchronousResponse.handleResponse(receivedPacket);
} else {
LOG.debug("Service: " + Integer.toHexString(receivedPacket.serviceId & 0xff) + ", command: " + Integer.toHexString(receivedPacket.commandId & 0xff) + ", handled by: " + handler.getClass());
if (handler.autoRemoveFromResponseHandler()) {
synchronized (handlers) {
handlers.remove(handler);
}
}
handler.handleResponse();
}
receivedPacket = null;
}
receivedPacket = null;
}
} while (left > 0);
}
}

View File

@ -19,20 +19,20 @@ public class TestHuaweiSupportProvider {
Mockito.verify(supportProvider.responseManager, Mockito.times(1)).handleData(data1);
}
@Test
public void testOnSocketReadMultiplePacket() {
byte[] expected = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B};
byte[] data1 = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B, (byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B};
HuaweiBRSupport support = new HuaweiBRSupport();
HuaweiSupportProvider supportProvider = new HuaweiSupportProvider(support);
supportProvider.responseManager = Mockito.mock(ResponseManager.class);
supportProvider.onSocketRead(data1);
Mockito.verify(supportProvider.responseManager, Mockito.times(2)).handleData(expected);
}
// @Test
// public void testOnSocketReadMultiplePacket() {
// byte[] expected = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B};
// byte[] data1 = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B, (byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B};
//
// HuaweiBRSupport support = new HuaweiBRSupport();
//
// HuaweiSupportProvider supportProvider = new HuaweiSupportProvider(support);
// supportProvider.responseManager = Mockito.mock(ResponseManager.class);
//
// supportProvider.onSocketRead(data1);
//
// Mockito.verify(supportProvider.responseManager, Mockito.times(2)).handleData(expected);
// }
@Test
public void testOnSocketReadPartialPacket() {
@ -50,4 +50,22 @@ public class TestHuaweiSupportProvider {
Mockito.verify(supportProvider.responseManager, Mockito.times(1)).handleData(data1);
Mockito.verify(supportProvider.responseManager, Mockito.times(1)).handleData(data2);
}
@Test
public void testOnSocketReadPartialPacket5a() {
byte[] data1 = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01};
byte[] data2 = {(byte) 0x5A, (byte) 0x91, (byte) 0x92, (byte) 0x99, (byte) 0x6B};
HuaweiBRSupport support = new HuaweiBRSupport();
HuaweiSupportProvider supportProvider = new HuaweiSupportProvider(support);
supportProvider.responseManager = Mockito.mock(ResponseManager.class);
supportProvider.onSocketRead(data1);
supportProvider.onSocketRead(data2);
Mockito.verify(supportProvider.responseManager, Mockito.times(1)).handleData(data1);
Mockito.verify(supportProvider.responseManager, Mockito.times(1)).handleData(data2);
}
}

View File

@ -467,4 +467,24 @@ public class TestResponseManager {
verify(request2, times(1)).handleResponse((HuaweiPacket) any());
verify(request2, times(0)).handleResponse();
}
@Test
public void testOnSocketReadMultiplePacketSplit() throws IllegalAccessException, HuaweiPacket.ParseException {
byte[] expected = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B};
byte[] data1 = {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01};
byte[] data2 = {(byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B, (byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B};
HuaweiPacket expectedPacket = new HuaweiPacket(supportProvider.getParamsProvider()).parse(expected);
AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class);
ResponseManager responseManager = new ResponseManager(supportProvider);
asynchronousResponseField.set(responseManager, mockAsynchronousResponse);
responseManager.handleData(data1);
responseManager.handleData(data2);
verify(mockAsynchronousResponse, times(2)).handleResponse(expectedPacket);
}
}