diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java index 77956bc93..6a7e6766c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java @@ -143,20 +143,26 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni protected TransactionBuilder initializeDevice(final TransactionBuilder builder) { builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); - if (getSupportedServices().contains(CommunicatorV2.UUID_SERVICE_GARMIN_ML_GFDI)) { - communicator = new CommunicatorV2(this); - } else if (getSupportedServices().contains(CommunicatorV1.UUID_SERVICE_GARMIN_GFDI)) { - communicator = new CommunicatorV1(this); - } else { - LOG.warn("Failed to find a known Garmin service"); - builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.NOT_CONNECTED, getContext())); - return builder; - } - if (getDevicePrefs().getBoolean(PREF_ALLOW_HIGH_MTU, true)) { builder.requestMtu(515); } + final CommunicatorV2 communicatorV2 = new CommunicatorV2(this); + if (communicatorV2.initializeDevice(builder)) { + communicator = communicatorV2; + } else { + // V2 did not manage to initialize, attempt V1 + final CommunicatorV1 communicatorV1 = new CommunicatorV1(this); + if (!communicatorV1.initializeDevice(builder)) { + // Neither V1 nor V2 worked, not a Garmin device? + LOG.warn("Failed to find a known Garmin service"); + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.NOT_CONNECTED, getContext())); + return builder; + } + + communicator = communicatorV1; + } + communicator.initializeDevice(builder); return builder; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/ICommunicator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/ICommunicator.java index 71d820b73..9ebbeaa4a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/ICommunicator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/ICommunicator.java @@ -10,7 +10,7 @@ public interface ICommunicator { void onMtuChanged(final int mtu); - void initializeDevice(TransactionBuilder builder); + boolean initializeDevice(TransactionBuilder builder); boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java index 80a6a3d73..530d62ea7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java @@ -6,40 +6,88 @@ import android.bluetooth.BluetoothGattCharacteristic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Arrays; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator; public class CommunicatorV1 implements ICommunicator { private static final Logger LOG = LoggerFactory.getLogger(CommunicatorV1.class); public static final UUID UUID_SERVICE_GARMIN_GFDI = UUID.fromString("6A4E2401-667B-11E3-949A-0800200C9A66"); + public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_SEND = UUID.fromString("6A4E4C80-667B-11E3-949A-0800200C9A66"); + public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE = UUID.fromString("6A4ECD28-667B-11E3-949A-0800200C9A66"); + + private BluetoothGattCharacteristic characteristicSend; + private BluetoothGattCharacteristic characteristicReceive; private final GarminSupport mSupport; + public final CobsCoDec cobsCoDec; + public int maxWriteSize = 20; + public CommunicatorV1(final GarminSupport garminSupport) { this.mSupport = garminSupport; + this.cobsCoDec = new CobsCoDec(); } @Override public void onMtuChanged(final int mtu) { - + maxWriteSize = mtu - 3; } @Override - public void initializeDevice(final TransactionBuilder builder) { - LOG.error("Initialization is not implemented for V1"); + public boolean initializeDevice(final TransactionBuilder builder) { + characteristicSend = mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_GFDI_SEND); + characteristicReceive = mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE); + + if (characteristicSend == null || characteristicReceive == null) { + LOG.warn("Failed to find V1 GFDI characteristics"); + return false; + } + + builder.notify(characteristicReceive, true); + + LOG.debug("Initializing as Garmin V1"); + + return true; } @Override public void sendMessage(final String taskName, final byte[] message) { + if (null == message) + return; + final byte[] payload = cobsCoDec.encode(message); + + final TransactionBuilder builder = new TransactionBuilder(taskName); + int remainingBytes = payload.length; + if (remainingBytes > maxWriteSize - 1) { + int position = 0; + while (remainingBytes > 0) { + final byte[] fragment = Arrays.copyOfRange(payload, position, position + Math.min(remainingBytes, maxWriteSize - 1)); + builder.write(characteristicSend, fragment); + position += fragment.length; + remainingBytes -= fragment.length; + } + } else { + builder.write(characteristicSend, payload); + } + builder.queue(this.mSupport.getQueue()); } @Override public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + if (characteristic.getUuid().equals(characteristicReceive.getUuid())) { + this.cobsCoDec.receivedBytes(characteristic.getValue()); + this.mSupport.onMessage(this.cobsCoDec.retrieveMessage()); + + return true; + } + return false; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java index f1d49baff..3c4c853bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java @@ -77,7 +77,7 @@ public class CommunicatorV2 implements ICommunicator { } @Override - public void initializeDevice(final TransactionBuilder builder) { + public boolean initializeDevice(final TransactionBuilder builder) { // Iterate through the known ML characteristics until we find a known pair // send characteristic = read characteristic + 0x10 (eg. 2810 / 2820) for (int i = 0x2810; i <= 0x2814; i++) { @@ -90,13 +90,13 @@ public class CommunicatorV2 implements ICommunicator { builder.notify(characteristicReceive, true); builder.write(characteristicSend, closeAllServices()); - return; + return true; } } LOG.warn("Failed to find any known ML characteristics"); - builder.add(new SetDeviceStateAction(mSupport.getDevice(), GBDevice.State.NOT_CONNECTED, mSupport.getContext())); + return false; } @Override