1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-01 03:55:47 +02:00

Garmin: further work for auth negotiation

Add status message parsing and change the reply logic for watch-initiated Auth (in attempt to fix #3986): before this changeset the phone would reply with a generic ACK and then send a request to the watch for setting the auth (with all zeroes);
after this changeset the phone replies with a specific auth ack/status message but it ignores what the watch requested and acknowledges back all zeroes.

Blindly implemented based on the legacy vivomoveHR code, not tested against real devices.
This commit is contained in:
Daniele Gobbetti 2024-08-14 10:04:32 +02:00
parent d1b4e013d3
commit 7fa5cd1be5
3 changed files with 90 additions and 3 deletions

View File

@ -4,6 +4,8 @@ import org.apache.commons.lang3.EnumUtils;
import java.util.EnumSet;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.AuthNegotiationStatusMessage;
public class AuthNegotiationMessage extends GFDIMessage {
private final int unknown;
@ -16,7 +18,7 @@ public class AuthNegotiationMessage extends GFDIMessage {
LOG.info("Message {}, unkByte: {}, flags: {}", garminMessage, unknown, requestedAuthFlags);
this.statusMessage = getStatusMessage();
this.statusMessage = new AuthNegotiationStatusMessage(garminMessage, Status.ACK, AuthNegotiationStatusMessage.AuthNegotiationStatus.GUESS_OK, 0, EnumSet.noneOf(AuthFlags.class));
}
public static AuthNegotiationMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
@ -38,10 +40,10 @@ public class AuthNegotiationMessage extends GFDIMessage {
writer.writeByte(0);
writer.writeInt((int) EnumUtils.generateBitVector(AuthFlags.class, EnumSet.noneOf(AuthFlags.class)));
return true;
return false;
}
private enum AuthFlags {
public enum AuthFlags {
UNK_00000001, //saw in logs
UNK_00000010,
UNK_00000100,

View File

@ -0,0 +1,83 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status;
import androidx.annotation.Nullable;
import org.apache.commons.lang3.EnumUtils;
import java.util.EnumSet;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.AuthNegotiationMessage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
public class AuthNegotiationStatusMessage extends GFDIStatusMessage {
private final Status status;
private final AuthNegotiationStatus authNegotiationStatus;
private final int unknown;
private final EnumSet<AuthNegotiationMessage.AuthFlags> authFlags;
private final boolean sendOutgoing;
public AuthNegotiationStatusMessage(GarminMessage garminMessage, Status status, AuthNegotiationStatus authNegotiationStatus, int unknown, EnumSet<AuthNegotiationMessage.AuthFlags> authFlags) {
this(garminMessage, status, authNegotiationStatus, unknown, authFlags, true);
}
public AuthNegotiationStatusMessage(GarminMessage garminMessage, Status status, AuthNegotiationStatus authNegotiationStatus, int unknown, EnumSet<AuthNegotiationMessage.AuthFlags> authFlags, boolean sendOutgoing) {
this.garminMessage = garminMessage;
this.status = status;
this.authNegotiationStatus = authNegotiationStatus;
this.unknown = unknown;
this.authFlags = authFlags;
this.sendOutgoing = sendOutgoing;
}
public static AuthNegotiationStatusMessage parseIncoming(MessageReader reader, GarminMessage garminMessage) {
final Status status = Status.fromCode(reader.readByte());
final int authNegotiationStatusCode = reader.readByte();
final AuthNegotiationStatus authNegotiationStatus = AuthNegotiationStatus.fromCode(authNegotiationStatusCode);
if (null == authNegotiationStatus) {
LOG.warn("Unknown auth negotiation status code {}", authNegotiationStatusCode);
return null;
}
final int unk = reader.readByte();
final EnumSet<AuthNegotiationMessage.AuthFlags> authFlags = AuthNegotiationMessage.AuthFlags.fromBitMask(reader.readInt());
switch (authNegotiationStatus) {
case GUESS_OK:
LOG.info("Received {}/{} for message {} unkByte: {}, flags: {}", status, authNegotiationStatus, garminMessage, unk, authFlags);
break;
default:
LOG.warn("Received {}/{} for message {} unkByte: {}, flags: {}", status, authNegotiationStatus, garminMessage, unk, authFlags);
}
return new AuthNegotiationStatusMessage(garminMessage, status, authNegotiationStatus, unk, authFlags, false);
}
@Override
protected boolean generateOutgoing() {
final MessageWriter writer = new MessageWriter(response);
writer.writeShort(0); // packet size will be filled below
writer.writeShort(GarminMessage.RESPONSE.getId());
writer.writeShort(garminMessage.getId());
writer.writeByte(status.ordinal());
writer.writeByte(authNegotiationStatus.ordinal());
writer.writeByte(unknown);
writer.writeInt((int) EnumUtils.generateBitVector(AuthNegotiationMessage.AuthFlags.class, authFlags));
return sendOutgoing;
}
public enum AuthNegotiationStatus {
GUESS_OK,
GUESS_KO,
;
@Nullable
public static AuthNegotiationStatus fromCode(final int code) {
for (final AuthNegotiationStatus authNegotiationStatus : AuthNegotiationStatus.values()) {
if (authNegotiationStatus.ordinal() == code) {
return authNegotiationStatus;
}
}
return null;
}
}
}

View File

@ -32,6 +32,8 @@ public abstract class GFDIStatusMessage extends GFDIMessage {
return FitDefinitionStatusMessage.parseIncoming(reader, originalGarminMessage);
} else if (GarminMessage.FIT_DATA.equals(originalGarminMessage)) {
return FitDataStatusMessage.parseIncoming(reader, originalGarminMessage);
} else if (GarminMessage.AUTH_NEGOTIATION.equals(originalGarminMessage)) {
return AuthNegotiationStatusMessage.parseIncoming(reader, originalGarminMessage);
} else {
final Status status = Status.fromCode(reader.readByte());