diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java index ea611ba9b..f9b8caf4d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java @@ -6,20 +6,15 @@ import android.os.Build; import android.util.Log; import android.widget.Toast; -import androidx.annotation.RequiresApi; - import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; -import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBException; -import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.PackageConfigHelper; -import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilter; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -31,11 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.SetDeviceStateRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationGetRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.connection.SetConnectionParametersRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileCloseRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileDeleteRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileVerifyRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.NotificationFilterPutRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayNotificationRequest; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.AnimationRequest; @@ -49,6 +40,10 @@ public class FossilWatchAdapter extends WatchAdapter { private FossilRequest fossilRequest; + private int MTU = 23; + + private String ITEM_MTU = "MTU"; + public FossilWatchAdapter(QHybridSupport deviceSupport) { super(deviceSupport); } @@ -57,16 +52,22 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void initialize() { playPairingAnimation(); - // queueWrite(new FileDeleteRequest((short) 0x0200)); - queueWrite(new RequestMtuRequest(512)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + queueWrite(new RequestMtuRequest(512)); + } queueWrite(new ConfigurationGetRequest(this)); - // queueWrite(new SetConnectionParametersRequest()); syncNotificationSettings(); queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED)); } + public int getMTU(){ + if(this.MTU < 0) throw new RuntimeException("MTU not configured"); + + return this.MTU; + } + @Override public void playPairingAnimation() { queueWrite(new AnimationRequest()); @@ -315,6 +316,11 @@ public class FossilWatchAdapter extends WatchAdapter { @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); + this.MTU = mtu; + + getDeviceSupport().getDevice().addDeviceInfo(new GenericItem(ITEM_MTU, String.valueOf(mtu))); + getDeviceSupport().getDevice().sendDeviceUpdateIntent(getContext()); + ((RequestMtuRequest)fossilRequest).setFinished(true); try { queueWrite(requestQueue.remove(0)); @@ -325,7 +331,6 @@ public class FossilWatchAdapter extends WatchAdapter { //TODO split to multiple methods instead of switch public void queueWrite(Request request, boolean priorise) { if(request instanceof RequestMtuRequest){ - //TODO mtu on older devices if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { new TransactionBuilder("requestMtu") .requestMtu(512) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java index 1fa05887f..d3060f50b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/RequestMtuRequest.java @@ -1,9 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil; +import android.os.Build; + +import androidx.annotation.RequiresApi; + public class RequestMtuRequest extends FossilRequest { private int mtu; private boolean finished = false; + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public RequestMtuRequest(int mtu) { this.mtu = mtu; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java index a9fd6a389..664fec3b5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java @@ -17,7 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos import nodomain.freeyourgadget.gadgetbridge.util.GB; public class FilePutRequest extends FossilRequest { - public enum UploadState{INITIALIZED, UPLOADING, CLOSING, UPLOADED} + public enum UploadState {INITIALIZED, UPLOADING, CLOSING, UPLOADED} public UploadState state; @@ -29,6 +29,8 @@ public class FilePutRequest extends FossilRequest { private FossilWatchAdapter adapter; + byte[] file; + public FilePutRequest(short handle, byte[] file, FossilWatchAdapter adapter) { this.handle = handle; this.adapter = adapter; @@ -42,7 +44,7 @@ public class FilePutRequest extends FossilRequest { this.data = buffer.array(); - prepareFilePackets(file); + this.file = file; state = UploadState.INITIALIZED; } @@ -54,7 +56,7 @@ public class FilePutRequest extends FossilRequest { @Override public void handleResponse(BluetoothGattCharacteristic characteristic) { byte[] value = characteristic.getValue(); - if(characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")) { + if (characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")) { int responseType = value[0] & 0x0F; log("response: " + responseType); switch (responseType) { @@ -63,15 +65,17 @@ public class FilePutRequest extends FossilRequest { throw new RuntimeException("wrong answer header"); } state = UploadState.UPLOADING; - byte[] initialPacket = packets.get(0); - BtLEQueue queue = adapter.getDeviceSupport().getQueue(); - new TransactionBuilder("file upload") - .write( - adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0004-957f-7d4a-34a6-74696673696d")), - initialPacket - ) - .queue(queue); + TransactionBuilder transactionBuilder = new TransactionBuilder("file upload"); + BluetoothGattCharacteristic uploadCharacteristic = adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0004-957f-7d4a-34a6-74696673696d")); + + this.prepareFilePackets(this.file); + + for (byte[] packet : packets) { + transactionBuilder.write(uploadCharacteristic, packet); + } + + transactionBuilder.queue(adapter.getDeviceSupport().getQueue()); break; } case 8: { @@ -101,34 +105,21 @@ public class FilePutRequest extends FossilRequest { // break; } - packetIndex++; - if (packetIndex < packets.size()) { - byte[] initialPacket = packets.get(packetIndex); + ByteBuffer buffer2 = ByteBuffer.allocate(3); + buffer2.order(ByteOrder.LITTLE_ENDIAN); + buffer2.put((byte) 4); + buffer2.putShort(this.handle); - new TransactionBuilder("file upload") - .write( - adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0004-957f-7d4a-34a6-74696673696d")), - initialPacket - ) - .queue(adapter.getDeviceSupport().getQueue()); - break; - } else { - ByteBuffer buffer2 = ByteBuffer.allocate(3); - buffer2.order(ByteOrder.LITTLE_ENDIAN); - buffer2.put((byte) 4); - buffer2.putShort(this.handle); + new TransactionBuilder("file close") + .write( + adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")), + buffer2.array() + ) + .queue(adapter.getDeviceSupport().getQueue()); - new TransactionBuilder("file close") - .write( - adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")), - buffer2.array() - ) - .queue(adapter.getDeviceSupport().getQueue()); - - this.state = UploadState.CLOSING; - break; - } + this.state = UploadState.CLOSING; + break; } case 4: { if (value.length == 9) return; @@ -184,18 +175,19 @@ public class FilePutRequest extends FossilRequest { } @Override - public boolean isFinished(){ + public boolean isFinished() { return this.state == UploadState.UPLOADED; } private void prepareFilePackets(byte[] file) { - ByteBuffer buffer = ByteBuffer.allocate(file.length + 13 + 4); + int maxPacketSize = adapter.getMTU() - 4; + + ByteBuffer buffer = ByteBuffer.allocate(file.length + 12 + 4); buffer.order(ByteOrder.LITTLE_ENDIAN); - buffer.put((byte)0); buffer.putShort(handle); - buffer.put((byte)2); - buffer.put((byte)0); + buffer.put((byte) 2); + buffer.put((byte) 0); buffer.putInt(0); buffer.putInt(file.length); @@ -206,10 +198,22 @@ public class FilePutRequest extends FossilRequest { crc.update(file); buffer.putInt((int) crc.getValue()); - packets.add(buffer.array()); + byte[] data = buffer.array(); + + int packetCount = (int) Math.ceil(data.length / (float) maxPacketSize); + + for (int i = 0; i < packetCount; i++) { + int currentPacketLength = Math.min(maxPacketSize, data.length - i * maxPacketSize); + byte[] packet = new byte[currentPacketLength + 1]; + packet[0] = (byte) i; + System.arraycopy(data, i * maxPacketSize, packet, 1, currentPacketLength); + + packets.add(packet); + } } - public void onFilePut(boolean success){} + public void onFilePut(boolean success) { + } @Override public byte[] getStartSequence() {