178 lines
7.7 KiB
Java
178 lines
7.7 KiB
Java
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.v2;
|
|
|
|
import android.bluetooth.BluetoothGatt;
|
|
import android.bluetooth.BluetoothGattCharacteristic;
|
|
|
|
import org.apache.commons.lang3.ArrayUtils;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
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;
|
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
|
|
|
public class CommunicatorV2 implements ICommunicator {
|
|
public static final UUID UUID_SERVICE_GARMIN_ML_GFDI = UUID.fromString("6A4E2800-667B-11E3-949A-0800200C9A66"); //VivomoveConstants.UUID_SERVICE_GARMIN_ML_GFDI;
|
|
public static final UUID UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND = UUID.fromString("6a4e2822-667b-11e3-949a-0800200c9a66"); //VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND;
|
|
public static final UUID UUID_CHARACTERISTIC_GARMIN_ML_GFDI_RECEIVE = UUID.fromString("6a4e2812-667b-11e3-949a-0800200c9a66"); //VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_ML_GFDI_RECEIVE;
|
|
|
|
public int maxWriteSize = 20; //VivomoveConstants.MAX_WRITE_SIZE
|
|
private static final Logger LOG = LoggerFactory.getLogger(CommunicatorV2.class);
|
|
public final CobsCoDec cobsCoDec;
|
|
private final GarminSupport mSupport;
|
|
private final long gadgetBridgeClientID = 2L;
|
|
private int gfdiHandle = 0;
|
|
|
|
public CommunicatorV2(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) {
|
|
|
|
builder.notify(this.mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_ML_GFDI_RECEIVE), true);
|
|
builder.write(this.mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND), closeAllServices());
|
|
}
|
|
|
|
@Override
|
|
public void sendMessage(final byte[] message) {
|
|
if (null == message)
|
|
return;
|
|
if (0 == gfdiHandle) {
|
|
LOG.error("CANNOT SENT GFDI MESSAGE, HANDLE NOT YET SET. MESSAGE {}", message);
|
|
return;
|
|
}
|
|
final byte[] payload = cobsCoDec.encode(message);
|
|
// LOG.debug("SENDING MESSAGE: {} - COBS ENCODED: {}", GB.hexdump(message), GB.hexdump(payload));
|
|
final TransactionBuilder builder = new TransactionBuilder("sendMessage()");
|
|
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(this.mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND), ArrayUtils.addAll(new byte[]{(byte) gfdiHandle}, fragment));
|
|
position += fragment.length;
|
|
remainingBytes -= fragment.length;
|
|
}
|
|
} else {
|
|
builder.write(this.mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND), ArrayUtils.addAll(new byte[]{(byte) gfdiHandle}, payload));
|
|
}
|
|
builder.queue(this.mSupport.getQueue());
|
|
}
|
|
|
|
@Override
|
|
public boolean onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
|
ByteBuffer message = ByteBuffer.wrap(characteristic.getValue()).order(ByteOrder.LITTLE_ENDIAN);
|
|
// LOG.debug("RECEIVED: {}", GB.hexdump(message.array()));
|
|
final byte handle = message.get();
|
|
if (0x00 == handle) { //handle management message
|
|
|
|
final byte type = message.get();
|
|
final long incomingClientID = message.getLong();
|
|
|
|
if (incomingClientID != this.gadgetBridgeClientID) {
|
|
LOG.debug("Ignoring incoming message, client ID is not ours. Message: {}", GB.hexdump(message.array()));
|
|
}
|
|
RequestType requestType = RequestType.fromCode(type);
|
|
if (null == requestType) {
|
|
LOG.error("Unknown request type. Message: {}", message.array());
|
|
return true;
|
|
}
|
|
switch (requestType) {
|
|
case REGISTER_ML_REQ: //register service request
|
|
case CLOSE_HANDLE_REQ: //close handle request
|
|
case CLOSE_ALL_REQ: //close all handles request
|
|
case UNK_REQ: //unknown request
|
|
LOG.warn("Received handle request, expecting responses. Message: {}", message.array());
|
|
case REGISTER_ML_RESP: //register service response
|
|
LOG.debug("Received register response. Message: {}", message.array());
|
|
final short registeredService = message.getShort();
|
|
final byte status = message.get();
|
|
if (0 == status && 1 == registeredService) { //success
|
|
this.gfdiHandle = message.get();
|
|
}
|
|
break;
|
|
case CLOSE_HANDLE_RESP: //close handle response
|
|
LOG.debug("Received close handle response. Message: {}", message.array());
|
|
break;
|
|
case CLOSE_ALL_RESP: //close all handles response
|
|
LOG.debug("Received close all handles response. Message: {}", message.array());
|
|
new TransactionBuilder("open GFDI")
|
|
.write(this.mSupport.getCharacteristic(UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND), registerGFDI())
|
|
.queue(this.mSupport.getQueue());
|
|
break;
|
|
case UNK_RESP: //unknown response
|
|
LOG.debug("Received unknown. Message: {}", message.array());
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
} else if (this.gfdiHandle == handle) {
|
|
|
|
byte[] partial = new byte[message.remaining()];
|
|
message.get(partial);
|
|
this.cobsCoDec.receivedBytes(partial);
|
|
|
|
this.mSupport.onMessage(this.cobsCoDec.retrieveMessage());
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected byte[] closeAllServices() {
|
|
ByteBuffer toSend = ByteBuffer.allocate(13);
|
|
toSend.order(ByteOrder.BIG_ENDIAN);
|
|
toSend.putShort((short) RequestType.CLOSE_ALL_REQ.ordinal()); //close all services
|
|
toSend.order(ByteOrder.LITTLE_ENDIAN);
|
|
toSend.putLong(this.gadgetBridgeClientID);
|
|
toSend.putShort((short) 0);
|
|
return toSend.array();
|
|
}
|
|
|
|
protected byte[] registerGFDI() {
|
|
ByteBuffer toSend = ByteBuffer.allocate(13);
|
|
toSend.order(ByteOrder.BIG_ENDIAN);
|
|
toSend.putShort((short) RequestType.REGISTER_ML_REQ.ordinal()); //register service request
|
|
toSend.order(ByteOrder.LITTLE_ENDIAN);
|
|
toSend.putLong(this.gadgetBridgeClientID);
|
|
toSend.putShort((short) 1); //service GFDI
|
|
return toSend.array();
|
|
}
|
|
|
|
enum RequestType {
|
|
REGISTER_ML_REQ,
|
|
REGISTER_ML_RESP,
|
|
CLOSE_HANDLE_REQ,
|
|
CLOSE_HANDLE_RESP,
|
|
UNK_HANDLE,
|
|
CLOSE_ALL_REQ,
|
|
CLOSE_ALL_RESP,
|
|
UNK_REQ,
|
|
UNK_RESP;
|
|
|
|
public static RequestType fromCode(final int code) {
|
|
for (final RequestType requestType : RequestType.values()) {
|
|
if (requestType.ordinal() == code) {
|
|
return requestType;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|