mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-25 03:16:51 +01:00
Garmin protocol: fixes
- fix DEVICE_SETTINGS message ID - put all status messages in own package - allow protobuf handler to change the returned status message to signal unsupported requests - fix various bugs
This commit is contained in:
parent
559a73cc5e
commit
afe41ee563
@ -30,9 +30,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.Conf
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MusicControlEntityUpdateMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MusicControlEntityUpdateMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufStatusMessage;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.SetDeviceSettingsMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.SetDeviceSettingsMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.SystemEventMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.SystemEventMessage;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.ProtobufStatusMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -100,6 +100,14 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
|||||||
|
|
||||||
evaluateGBDeviceEvent(parsedMessage.getGBDeviceEvent());
|
evaluateGBDeviceEvent(parsedMessage.getGBDeviceEvent());
|
||||||
|
|
||||||
|
if (parsedMessage instanceof ProtobufMessage) {
|
||||||
|
ProtobufMessage protobufMessage = protocolBufferHandler.processIncoming((ProtobufMessage) parsedMessage);
|
||||||
|
if (protobufMessage != null) {
|
||||||
|
communicator.sendMessage(protobufMessage.getOutgoingMessage());
|
||||||
|
communicator.sendMessage(protobufMessage.getAckBytestream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
communicator.sendMessage(parsedMessage.getAckBytestream());
|
communicator.sendMessage(parsedMessage.getAckBytestream());
|
||||||
|
|
||||||
byte[] response = parsedMessage.getOutgoingMessage();
|
byte[] response = parsedMessage.getOutgoingMessage();
|
||||||
@ -112,14 +120,6 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
|
|||||||
completeInitialization();
|
completeInitialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedMessage instanceof ProtobufMessage) {
|
|
||||||
ProtobufMessage protobufMessage = protocolBufferHandler.processIncoming((ProtobufMessage) parsedMessage);
|
|
||||||
if (protobufMessage != null) {
|
|
||||||
communicator.sendMessage(protobufMessage.getOutgoingMessage());
|
|
||||||
communicator.sendMessage(protobufMessage.getAckBytestream());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedMessage instanceof ProtobufStatusMessage) {
|
if (parsedMessage instanceof ProtobufStatusMessage) {
|
||||||
ProtobufMessage protobufMessage = protocolBufferHandler.processIncoming((ProtobufStatusMessage) parsedMessage);
|
ProtobufMessage protobufMessage = protocolBufferHandler.processIncoming((ProtobufStatusMessage) parsedMessage);
|
||||||
if (protobufMessage != null) {
|
if (protobufMessage != null) {
|
||||||
|
@ -20,7 +20,7 @@ import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiFindMyWatch;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiSmartProto;
|
import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiSmartProto;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufStatusMessage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.ProtobufStatusMessage;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
|
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
|
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
|
||||||
@ -75,13 +75,14 @@ public class ProtocolBufferHandler {
|
|||||||
}
|
}
|
||||||
if (!processed) {
|
if (!processed) {
|
||||||
LOG.warn("Unknown protobuf request: {}", smart);
|
LOG.warn("Unknown protobuf request: {}", smart);
|
||||||
|
message.setStatusMessage(new ProtobufStatusMessage(message.getMessageType(), GFDIMessage.Status.ACK, message.getRequestId(), message.getDataOffset(), ProtobufStatusMessage.ProtobufChunkStatus.DISCARDED, ProtobufStatusMessage.ProtobufStatusCode.UNKNOWN_REQUEST_ID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProtobufMessage processIncoming(ProtobufStatusMessage statusMessage) {
|
public ProtobufMessage processIncoming(ProtobufStatusMessage statusMessage) {
|
||||||
LOG.info("Processing protobuf status message #{}@{}: status={}, error={}", statusMessage.getRequestId(), statusMessage.getDataOffset(), statusMessage.getProtobufStatus(), statusMessage.getError());
|
LOG.info("Processing protobuf status message #{}@{}: status={}, error={}", statusMessage.getRequestId(), statusMessage.getDataOffset(), statusMessage.getProtobufChunkStatus(), statusMessage.getProtobufStatusCode());
|
||||||
//TODO: check status and react accordingly, right now we blindly proceed to next chunk
|
//TODO: check status and react accordingly, right now we blindly proceed to next chunk
|
||||||
if (chunkedFragmentsMap.containsKey(statusMessage.getRequestId()) && statusMessage.isOK()) {
|
if (chunkedFragmentsMap.containsKey(statusMessage.getRequestId()) && statusMessage.isOK()) {
|
||||||
final ProtobufFragment protobufFragment = chunkedFragmentsMap.get(statusMessage.getRequestId());
|
final ProtobufFragment protobufFragment = chunkedFragmentsMap.get(statusMessage.getRequestId());
|
||||||
|
@ -7,8 +7,11 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.GFDIStatusMessage;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.GenericStatusMessage;
|
||||||
|
|
||||||
public abstract class GFDIMessage {
|
public abstract class GFDIMessage {
|
||||||
public static final int MESSAGE_RESPONSE = 5000; //TODO: MESSAGE_STATUS is a better name?
|
public static final int MESSAGE_RESPONSE = 5000; //TODO: MESSAGE_STATUS is a better name?
|
||||||
@ -51,9 +54,9 @@ public abstract class GFDIMessage {
|
|||||||
|
|
||||||
final int messageType = messageReader.readShort();
|
final int messageType = messageReader.readShort();
|
||||||
try {
|
try {
|
||||||
// Class<? extends GFDIMessage> objectClass = GarminMessage.fromId(messageType);
|
// Class<? extends GFDIMessage> objectClass = GarminMessage.getClassFromId(messageType);
|
||||||
Method m = GarminMessage.fromId(messageType).getMethod("parseIncoming", MessageReader.class, int.class);
|
Method m = GarminMessage.getClassFromId(messageType).getMethod("parseIncoming", MessageReader.class, int.class);
|
||||||
return GarminMessage.fromId(messageType).cast(m.invoke(null, messageReader, messageType));
|
return GarminMessage.getClassFromId(messageType).cast(m.invoke(null, messageReader, messageType));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("UNHANDLED GFDI MESSAGE TYPE {}, MESSAGE {}", messageType, message);
|
LOG.error("UNHANDLED GFDI MESSAGE TYPE {}, MESSAGE {}", messageType, message);
|
||||||
return new UnhandledMessage(messageType);
|
return new UnhandledMessage(messageType);
|
||||||
@ -65,6 +68,7 @@ public abstract class GFDIMessage {
|
|||||||
public byte[] getOutgoingMessage() {
|
public byte[] getOutgoingMessage() {
|
||||||
response.clear();
|
response.clear();
|
||||||
boolean toSend = generateOutgoing();
|
boolean toSend = generateOutgoing();
|
||||||
|
response.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
if (!toSend)
|
if (!toSend)
|
||||||
return null;
|
return null;
|
||||||
addLengthAndChecksum();
|
addLengthAndChecksum();
|
||||||
@ -99,6 +103,7 @@ public abstract class GFDIMessage {
|
|||||||
RESPONSE(5000, GFDIStatusMessage.class), //TODO: STATUS is a better name?
|
RESPONSE(5000, GFDIStatusMessage.class), //TODO: STATUS is a better name?
|
||||||
SYSTEM_EVENT(5030, SystemEventMessage.class),
|
SYSTEM_EVENT(5030, SystemEventMessage.class),
|
||||||
DEVICE_INFORMATION(5024, DeviceInformationMessage.class),
|
DEVICE_INFORMATION(5024, DeviceInformationMessage.class),
|
||||||
|
DEVICE_SETTINGS(5026, SetDeviceSettingsMessage.class),
|
||||||
FIND_MY_PHONE(5039, FindMyPhoneRequestMessage.class),
|
FIND_MY_PHONE(5039, FindMyPhoneRequestMessage.class),
|
||||||
CANCEL_FIND_MY_PHONE(5040, FindMyPhoneRequestMessage.class),
|
CANCEL_FIND_MY_PHONE(5040, FindMyPhoneRequestMessage.class),
|
||||||
MUSIC_CONTROL(5041, MusicControlMessage.class),
|
MUSIC_CONTROL(5041, MusicControlMessage.class),
|
||||||
@ -117,7 +122,7 @@ public abstract class GFDIMessage {
|
|||||||
this.objectClass = objectClass;
|
this.objectClass = objectClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Class<? extends GFDIMessage> fromId(final int id) {
|
public static Class<? extends GFDIMessage> getClassFromId(final int id) {
|
||||||
for (final GarminMessage garminMessage : GarminMessage.values()) {
|
for (final GarminMessage garminMessage : GarminMessage.values()) {
|
||||||
if (garminMessage.getId() == id) {
|
if (garminMessage.getId() == id) {
|
||||||
return garminMessage.getObjectClass();
|
return garminMessage.getObjectClass();
|
||||||
@ -126,6 +131,14 @@ public abstract class GFDIMessage {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GarminMessage fromId(final int id) {
|
||||||
|
for (final GarminMessage garminMessage : GarminMessage.values()) {
|
||||||
|
if (garminMessage.getId() == id) {
|
||||||
|
return garminMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class GFDIStatusMessage extends GFDIMessage {
|
|
||||||
Status status;
|
|
||||||
|
|
||||||
public static GFDIStatusMessage parseIncoming(MessageReader reader, int messageType) {
|
|
||||||
final int requestMessageType = reader.readShort();
|
|
||||||
if (GarminMessage.PROTOBUF_REQUEST.getId() == requestMessageType || GarminMessage.PROTOBUF_RESPONSE.getId() == requestMessageType) {
|
|
||||||
return ProtobufStatusMessage.parseIncoming(reader, messageType);
|
|
||||||
} else {
|
|
||||||
final Status status = Status.fromCode(reader.readByte());
|
|
||||||
|
|
||||||
reader.warnIfLeftover();
|
|
||||||
return new GenericStatusMessage(messageType, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean generateOutgoing() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Status getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -13,7 +13,6 @@ public class MessageReader {
|
|||||||
protected static final Logger LOG = LoggerFactory.getLogger(MessageReader.class);
|
protected static final Logger LOG = LoggerFactory.getLogger(MessageReader.class);
|
||||||
|
|
||||||
private final ByteBuffer byteBuffer;
|
private final ByteBuffer byteBuffer;
|
||||||
|
|
||||||
private final int payloadSize;
|
private final int payloadSize;
|
||||||
|
|
||||||
public MessageReader(byte[] data) {
|
public MessageReader(byte[] data) {
|
||||||
@ -33,6 +32,10 @@ public class MessageReader {
|
|||||||
return !byteBuffer.hasRemaining();
|
return !byteBuffer.hasRemaining();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEndOfPayload() {
|
||||||
|
return byteBuffer.position() >= payloadSize - 2;
|
||||||
|
}
|
||||||
|
|
||||||
public int getPosition() {
|
public int getPosition() {
|
||||||
return byteBuffer.position();
|
return byteBuffer.position();
|
||||||
}
|
}
|
||||||
@ -99,7 +102,6 @@ public class MessageReader {
|
|||||||
return byteBuffer.capacity();
|
return byteBuffer.capacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void checkSize() {
|
private void checkSize() {
|
||||||
if (payloadSize > getCapacity()) {
|
if (payloadSize > getCapacity()) {
|
||||||
LOG.error("Received GFDI packet with invalid length: {} vs {}", payloadSize, getCapacity());
|
LOG.error("Received GFDI packet with invalid length: {} vs {}", payloadSize, getCapacity());
|
||||||
@ -116,12 +118,13 @@ public class MessageReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void warnIfLeftover() {
|
public void warnIfLeftover() {
|
||||||
if (byteBuffer.hasRemaining() && byteBuffer.position() < (byteBuffer.limit() - 2)) {
|
if (byteBuffer.hasRemaining() && byteBuffer.position() < (byteBuffer.limit() - 2)) {
|
||||||
|
int pos = byteBuffer.position();
|
||||||
int numBytes = (byteBuffer.limit() - 2) - byteBuffer.position();
|
int numBytes = (byteBuffer.limit() - 2) - byteBuffer.position();
|
||||||
byte[] leftover = new byte[numBytes];
|
byte[] leftover = new byte[numBytes];
|
||||||
byteBuffer.get(leftover);
|
byteBuffer.get(leftover);
|
||||||
|
byteBuffer.position(pos);
|
||||||
LOG.warn("Leftover bytes when parsing message. Bytes: {}, complete message: {}", GB.hexdump(leftover), GB.hexdump(byteBuffer.array()));
|
LOG.warn("Leftover bytes when parsing message. Bytes: {}, complete message: {}", GB.hexdump(leftover), GB.hexdump(byteBuffer.array()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
||||||
|
|
||||||
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufStatusMessage.ProtobufStatusCode.NO_ERROR;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.GenericStatusMessage;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.ProtobufStatusMessage;
|
||||||
|
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.ProtobufStatusMessage.ProtobufChunkStatus.KEPT;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.ProtobufStatusMessage.ProtobufStatusCode.NO_ERROR;
|
||||||
|
|
||||||
public class ProtobufMessage extends GFDIMessage {
|
public class ProtobufMessage extends GFDIMessage {
|
||||||
|
|
||||||
@ -14,10 +18,6 @@ public class ProtobufMessage extends GFDIMessage {
|
|||||||
private final byte[] messageBytes;
|
private final byte[] messageBytes;
|
||||||
private final boolean sendOutgoing;
|
private final boolean sendOutgoing;
|
||||||
|
|
||||||
public ProtobufMessage(int messageType, int requestId, int dataOffset, int totalProtobufLength, int protobufDataLength, byte[] messageBytes) {
|
|
||||||
this(messageType, requestId, dataOffset, totalProtobufLength, protobufDataLength, messageBytes, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProtobufMessage(int messageType, int requestId, int dataOffset, int totalProtobufLength, int protobufDataLength, byte[] messageBytes, boolean sendOutgoing) {
|
public ProtobufMessage(int messageType, int requestId, int dataOffset, int totalProtobufLength, int protobufDataLength, byte[] messageBytes, boolean sendOutgoing) {
|
||||||
this.messageType = messageType;
|
this.messageType = messageType;
|
||||||
this.requestId = requestId;
|
this.requestId = requestId;
|
||||||
@ -30,9 +30,16 @@ public class ProtobufMessage extends GFDIMessage {
|
|||||||
if (isComplete()) {
|
if (isComplete()) {
|
||||||
this.statusMessage = new GenericStatusMessage(messageType, GFDIMessage.Status.ACK);
|
this.statusMessage = new GenericStatusMessage(messageType, GFDIMessage.Status.ACK);
|
||||||
} else {
|
} else {
|
||||||
this.statusMessage = new ProtobufStatusMessage(messageType, GFDIMessage.Status.ACK, requestId, dataOffset, NO_ERROR, NO_ERROR);
|
this.statusMessage = new ProtobufStatusMessage(messageType, GFDIMessage.Status.ACK, requestId, dataOffset, KEPT, NO_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public ProtobufMessage(int messageType, int requestId, int dataOffset, int totalProtobufLength, int protobufDataLength, byte[] messageBytes) {
|
||||||
|
this(messageType, requestId, dataOffset, totalProtobufLength, protobufDataLength, messageBytes, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatusMessage(ProtobufStatusMessage protobufStatusMessage) {
|
||||||
|
this.statusMessage = protobufStatusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
public static ProtobufMessage parseIncoming(MessageReader reader, int messageType) {
|
public static ProtobufMessage parseIncoming(MessageReader reader, int messageType) {
|
||||||
final int requestID = reader.readShort();
|
final int requestID = reader.readShort();
|
||||||
|
@ -17,7 +17,7 @@ public class SetDeviceSettingsMessage extends GFDIMessage {
|
|||||||
protected boolean generateOutgoing() {
|
protected boolean generateOutgoing() {
|
||||||
final MessageWriter writer = new MessageWriter(response);
|
final MessageWriter writer = new MessageWriter(response);
|
||||||
writer.writeShort(0); // packet size will be filled below
|
writer.writeShort(0); // packet size will be filled below
|
||||||
writer.writeShort(GarminMessage.DEVICE_INFORMATION.getId());
|
writer.writeShort(GarminMessage.DEVICE_SETTINGS.getId());
|
||||||
writer.writeByte(settings.size());
|
writer.writeByte(settings.size());
|
||||||
for (Map.Entry<GarminDeviceSetting, Object> settingPair : settings.entrySet()) {
|
for (Map.Entry<GarminDeviceSetting, Object> settingPair : settings.entrySet()) {
|
||||||
final GarminDeviceSetting setting = settingPair.getKey();
|
final GarminDeviceSetting setting = settingPair.getKey();
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.GenericStatusMessage;
|
||||||
|
|
||||||
public class UnhandledMessage extends GFDIMessage {
|
public class UnhandledMessage extends GFDIMessage {
|
||||||
|
|
||||||
private final int messageType;
|
private final int messageType;
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status;
|
||||||
|
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageReader;
|
||||||
|
|
||||||
|
public abstract class GFDIStatusMessage extends GFDIMessage {
|
||||||
|
Status status;
|
||||||
|
|
||||||
|
public static GFDIStatusMessage parseIncoming(MessageReader reader, int messageType) {
|
||||||
|
final GarminMessage garminMessage = GFDIMessage.GarminMessage.fromId(reader.readShort());
|
||||||
|
if (GarminMessage.PROTOBUF_REQUEST.equals(garminMessage) || GarminMessage.PROTOBUF_RESPONSE.equals(garminMessage)) {
|
||||||
|
return ProtobufStatusMessage.parseIncoming(reader, messageType);
|
||||||
|
} else {
|
||||||
|
final Status status = Status.fromCode(reader.readByte());
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case ACK:
|
||||||
|
LOG.info("Received ACK for message {}", garminMessage.name());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.warn("Received {} for message {}", status, garminMessage.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.warnIfLeftover();
|
||||||
|
return new GenericStatusMessage(messageType, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean generateOutgoing() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
|
||||||
|
|
||||||
public class GenericStatusMessage extends GFDIStatusMessage {
|
public class GenericStatusMessage extends GFDIStatusMessage {
|
||||||
|
|
@ -1,27 +1,31 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages;
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageReader;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
|
||||||
|
|
||||||
public class ProtobufStatusMessage extends GFDIStatusMessage {
|
public class ProtobufStatusMessage extends GFDIStatusMessage {
|
||||||
|
|
||||||
private final Status status;
|
private final Status status;
|
||||||
private final int requestId;
|
private final int requestId;
|
||||||
private final int dataOffset;
|
private final int dataOffset;
|
||||||
private final ProtobufStatusCode protobufStatus;
|
private final ProtobufChunkStatus protobufChunkStatus;
|
||||||
private final ProtobufStatusCode error; //TODO: why is this duplicated?
|
private final ProtobufStatusCode protobufStatusCode;
|
||||||
private final int messageType;
|
private final int messageType;
|
||||||
private final boolean sendOutgoing;
|
private final boolean sendOutgoing;
|
||||||
|
|
||||||
public ProtobufStatusMessage(int messageType, Status status, int requestId, int dataOffset, ProtobufStatusCode protobufStatus, ProtobufStatusCode error) {
|
public ProtobufStatusMessage(int messageType, Status status, int requestId, int dataOffset, ProtobufChunkStatus protobufChunkStatus, ProtobufStatusCode protobufStatusCode) {
|
||||||
this(messageType, status, requestId, dataOffset, protobufStatus, error, true);
|
this(messageType, status, requestId, dataOffset, protobufChunkStatus, protobufStatusCode, true);
|
||||||
}
|
}
|
||||||
public ProtobufStatusMessage(int messageType, Status status, int requestId, int dataOffset, ProtobufStatusCode protobufStatus, ProtobufStatusCode error, boolean sendOutgoing) {
|
|
||||||
|
public ProtobufStatusMessage(int messageType, Status status, int requestId, int dataOffset, ProtobufChunkStatus protobufChunkStatus, ProtobufStatusCode protobufStatusCode, boolean sendOutgoing) {
|
||||||
this.messageType = messageType;
|
this.messageType = messageType;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.requestId = requestId;
|
this.requestId = requestId;
|
||||||
this.dataOffset = dataOffset;
|
this.dataOffset = dataOffset;
|
||||||
this.protobufStatus = protobufStatus;
|
this.protobufChunkStatus = protobufChunkStatus;
|
||||||
this.error = error;
|
this.protobufStatusCode = protobufStatusCode;
|
||||||
this.sendOutgoing = sendOutgoing;
|
this.sendOutgoing = sendOutgoing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +33,7 @@ public class ProtobufStatusMessage extends GFDIStatusMessage {
|
|||||||
final Status status = Status.fromCode(reader.readByte());
|
final Status status = Status.fromCode(reader.readByte());
|
||||||
final int requestID = reader.readShort();
|
final int requestID = reader.readShort();
|
||||||
final int dataOffset = reader.readInt();
|
final int dataOffset = reader.readInt();
|
||||||
final ProtobufStatusCode protobufStatus = ProtobufStatusCode.fromCode(reader.readByte());
|
final ProtobufChunkStatus protobufStatus = ProtobufChunkStatus.fromCode(reader.readByte());
|
||||||
final ProtobufStatusCode error = ProtobufStatusCode.fromCode(reader.readByte());
|
final ProtobufStatusCode error = ProtobufStatusCode.fromCode(reader.readByte());
|
||||||
|
|
||||||
reader.warnIfLeftover();
|
reader.warnIfLeftover();
|
||||||
@ -40,12 +44,12 @@ public class ProtobufStatusMessage extends GFDIStatusMessage {
|
|||||||
return dataOffset;
|
return dataOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProtobufStatusCode getProtobufStatus() {
|
public ProtobufChunkStatus getProtobufChunkStatus() {
|
||||||
return protobufStatus;
|
return protobufChunkStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProtobufStatusCode getError() {
|
public ProtobufStatusCode getProtobufStatusCode() {
|
||||||
return error;
|
return protobufStatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMessageType() {
|
public int getMessageType() {
|
||||||
@ -58,8 +62,8 @@ public class ProtobufStatusMessage extends GFDIStatusMessage {
|
|||||||
|
|
||||||
public boolean isOK() {
|
public boolean isOK() {
|
||||||
return this.status.equals(Status.ACK) &&
|
return this.status.equals(Status.ACK) &&
|
||||||
this.protobufStatus.equals(ProtobufStatusCode.NO_ERROR) &&
|
this.protobufChunkStatus.equals(ProtobufChunkStatus.KEPT) &&
|
||||||
this.error.equals(ProtobufStatusCode.NO_ERROR);
|
this.protobufStatusCode.equals(ProtobufStatusCode.NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -71,8 +75,8 @@ public class ProtobufStatusMessage extends GFDIStatusMessage {
|
|||||||
writer.writeByte(status.ordinal());
|
writer.writeByte(status.ordinal());
|
||||||
writer.writeShort(requestId);
|
writer.writeShort(requestId);
|
||||||
writer.writeInt(dataOffset);
|
writer.writeInt(dataOffset);
|
||||||
writer.writeByte(protobufStatus.code);
|
writer.writeByte(protobufChunkStatus.ordinal());
|
||||||
writer.writeByte(error.code);
|
writer.writeByte(protobufStatusCode.code);
|
||||||
return sendOutgoing;
|
return sendOutgoing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,9 +84,25 @@ public class ProtobufStatusMessage extends GFDIStatusMessage {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ProtobufChunkStatus { //based on the observations of the combination with the StatusCode
|
||||||
|
KEPT,
|
||||||
|
DISCARDED,
|
||||||
|
;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static ProtobufChunkStatus fromCode(final int code) {
|
||||||
|
for (final ProtobufChunkStatus status : ProtobufChunkStatus.values()) {
|
||||||
|
if (status.ordinal() == code) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum ProtobufStatusCode {
|
public enum ProtobufStatusCode {
|
||||||
NO_ERROR(0),
|
NO_ERROR(0),
|
||||||
UNKNOWN_ERROR(1),
|
|
||||||
UNKNOWN_REQUEST_ID(100),
|
UNKNOWN_REQUEST_ID(100),
|
||||||
DUPLICATE_PACKET(101),
|
DUPLICATE_PACKET(101),
|
||||||
MISSING_PACKET(102),
|
MISSING_PACKET(102),
|
Loading…
Reference in New Issue
Block a user