From 0e985d5461aeb9854e5e448353c93ee5ce35b2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Sat, 17 Aug 2024 14:41:30 +0100 Subject: [PATCH] Garmin: Fix crash on large gpx import --- .../service/devices/garmin/fit/FitFile.java | 21 +++++++++++++++++-- .../garmin/messages/MessageWriter.java | 7 ++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitFile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitFile.java index 4ac3e83d2..d6ff0cf04 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitFile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/fit/FitFile.java @@ -128,7 +128,7 @@ public class FitFile { if (!canGenerateOutput) throw new IllegalArgumentException("Generation of previously parsed FIT file not supported."); - MessageWriter temporary = new MessageWriter(); + MessageWriter temporary = new MessageWriter(writer.getLimit()); temporary.setByteOrder(ByteOrder.LITTLE_ENDIAN); RecordDefinition prevDefinition = null; for (final RecordData rd : dataRecords) { @@ -147,7 +147,24 @@ public class FitFile { } public byte[] getOutgoingMessage() { - final MessageWriter writer = new MessageWriter(); + // Compute the worst case scenario buffer size for the fit file + // A ~1.6MB gpx file with ~16k points results in a ~320KB buffer, ~150KB of which get actually used + final int dataRecordsSize = dataRecords.stream() + .mapToInt(r -> { + // Worst case scenario, for each data record + + // one distinct record definition: 5 bytes + (number of field definitions * 3 + 1) + final List definitions = r.getRecordDefinition().getFieldDefinitions(); + final int recordDefinitionOverhead = 5 + (definitions != null ? definitions.size() * 3 + 1 : 0); + + // 1 + size of the value holder + final int dataRecordOverhead = 1 + r.valueHolder.limit(); + + return recordDefinitionOverhead + dataRecordOverhead; + }).sum(); + + // Final size = 14b header + data records + 2b crc + final MessageWriter writer = new MessageWriter(14 + dataRecordsSize + 2); this.generateOutgoingDataPayload(writer); return writer.getBytes(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/MessageWriter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/MessageWriter.java index 2ece083a1..dc5ae50cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/MessageWriter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/MessageWriter.java @@ -57,8 +57,6 @@ public class MessageWriter { final int size = bytes.length; if (size > 255) throw new IllegalArgumentException("Too long string"); - if (byteBuffer.position() + 1 + size > byteBuffer.capacity()) - throw new IllegalStateException(); byteBuffer.put((byte) size); byteBuffer.put(bytes); } @@ -76,12 +74,15 @@ public class MessageWriter { return byteBuffer.position(); } + public int getLimit() { + return byteBuffer.limit(); + } + public void writeBytes(byte[] bytes) { writeBytes(bytes, 0, bytes.length); } public void writeBytes(byte[] bytes, int offset, int size) { - if (byteBuffer.position() + size > byteBuffer.capacity()) throw new IllegalStateException(); byteBuffer.put(Arrays.copyOfRange(bytes, offset, offset + size)); } }