diff --git a/.idea/dictionaries/t.xml b/.idea/dictionaries/t.xml index c25502eb5e..a9f3a021ad 100644 --- a/.idea/dictionaries/t.xml +++ b/.idea/dictionaries/t.xml @@ -161,6 +161,7 @@ trofimov tysiak uart + uihh uint utsob uuids diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainer.java index 23d51d75c3..978d271765 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainer.java @@ -28,6 +28,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; @@ -45,6 +46,10 @@ public class UIHHContainer { return files; } + public List getFileTypes() { + return files.stream().map(FileEntry::getType).collect(Collectors.toList()); + } + public byte[] getFile(final FileType fileType) { for (final FileEntry file : files) { if (file.getType() == fileType) { @@ -91,8 +96,8 @@ public class UIHHContainer { return null; } - final int crc32 = BLETypeConversions.toUint32(ArrayUtils.subarray(bytes, 12, 12 + 4)); - final int length = BLETypeConversions.toUint32(ArrayUtils.subarray(bytes, 22, 22 + 4)); + final int crc32 = BLETypeConversions.toUint32(bytes, 12); + final int length = BLETypeConversions.toUint32(bytes, 22); if (length + 32 != bytes.length) { LOG.error("Length mismatch between header and bytes: {}/{}", length, bytes.length); @@ -122,14 +127,14 @@ public class UIHHContainer { final FileType type = FileType.fromValue(bytes[i]); if (type == null) { - LOG.error("Unknown type byte {} at position {}", String.format("0x%x", bytes[i], i)); + LOG.error("Unknown type byte {} at position {}", String.format("0x%x", bytes[i]), i); return null; } i++; - final int fileLength = BLETypeConversions.toUint32(ArrayUtils.subarray(bytes, i, i + 4)); + final int fileLength = BLETypeConversions.toUint32(bytes, i); i += 4; - final int fileCrc32 = BLETypeConversions.toUint32(ArrayUtils.subarray(bytes, i, i + 4)); + final int fileCrc32 = BLETypeConversions.toUint32(bytes, i); i += 4; if (i + fileLength > bytes.length) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/operations/ZeppOsAgpsFile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/operations/ZeppOsAgpsFile.java index 9b2727af54..795c2effa1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/operations/ZeppOsAgpsFile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/operations/ZeppOsAgpsFile.java @@ -21,6 +21,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.UIHHContainer; import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile; @@ -29,18 +31,22 @@ import nodomain.freeyourgadget.gadgetbridge.util.ZipFileException; public class ZeppOsAgpsFile { private static final Logger LOG = LoggerFactory.getLogger(ZeppOsAgpsFile.class); - private final byte[] zipBytes; + private final byte[] fileBytes; - public ZeppOsAgpsFile(final byte[] zipBytes) { - this.zipBytes = zipBytes; + public ZeppOsAgpsFile(final byte[] fileBytes) { + this.fileBytes = fileBytes; } public boolean isValid() { - if (!GBZipFile.isZipFile(zipBytes)) { - return false; + if (GBZipFile.isZipFile(fileBytes)) { + return isValidAsEpoZip(); + } else { + return isValidAsUihh(); } + } - final GBZipFile zipFile = new GBZipFile(zipBytes); + private boolean isValidAsEpoZip() { + final GBZipFile zipFile = new GBZipFile(fileBytes); try { final byte[] manifestBin = zipFile.getFileFromZip("META-INF/MANIFEST.MF"); @@ -61,22 +67,64 @@ public class ZeppOsAgpsFile { LOG.error("Failed to parse read MANIFEST or check file", e); } - return false; + return true; + } + + private boolean isValidAsUihh() { + final UIHHContainer uihh = UIHHContainer.fromRawBytes(fileBytes); + if (uihh == null) { + return false; + } + + final List fileTypes = uihh.getFileTypes(); + final List expectedFileTypes = Arrays.asList( + UIHHContainer.FileType.GPS_ALM_BIN, + UIHHContainer.FileType.GLN_ALM_BIN, + UIHHContainer.FileType.LLE_BDS_LLE, + UIHHContainer.FileType.LLE_GPS_LLE, + UIHHContainer.FileType.LLE_GLO_LLE, + UIHHContainer.FileType.LLE_GAL_LLE, + UIHHContainer.FileType.LLE_QZSS_LLE + ); + + if (fileTypes.size() != expectedFileTypes.size()) { + LOG.warn("uihh file types mismatch - expected {}, found {}", expectedFileTypes.size(), fileTypes.size()); + return false; + } + + for (final UIHHContainer.FileType fileType : expectedFileTypes) { + if (!fileTypes.contains(fileType)) { + LOG.warn("uihh is missing file type {}", fileType); + return false; + } + } + + return true; } public byte[] getUihhBytes() { - final UIHHContainer uihh = new UIHHContainer(); + if (GBZipFile.isZipFile(fileBytes)) { + // EPO zip - repackage into UIHH + final UIHHContainer uihh = new UIHHContainer(); - final GBZipFile zipFile = new GBZipFile(zipBytes); + final GBZipFile zipFile = new GBZipFile(fileBytes); - try { - uihh.addFile(UIHHContainer.FileType.AGPS_EPO_GR_3, zipFile.getFileFromZip("EPO_GR_3.DAT")); - uihh.addFile(UIHHContainer.FileType.AGPS_EPO_GAL_7, zipFile.getFileFromZip("EPO_GAL_7.DAT")); - uihh.addFile(UIHHContainer.FileType.AGPS_EPO_BDS_3, zipFile.getFileFromZip("EPO_BDS_3.DAT")); - } catch (final ZipFileException e) { - throw new IllegalStateException("Failed to read file from zip", e); + try { + uihh.addFile(UIHHContainer.FileType.AGPS_EPO_GR_3, zipFile.getFileFromZip("EPO_GR_3.DAT")); + uihh.addFile(UIHHContainer.FileType.AGPS_EPO_GAL_7, zipFile.getFileFromZip("EPO_GAL_7.DAT")); + uihh.addFile(UIHHContainer.FileType.AGPS_EPO_BDS_3, zipFile.getFileFromZip("EPO_BDS_3.DAT")); + } catch (final ZipFileException e) { + throw new IllegalStateException("Failed to read file from zip", e); + } + + return uihh.toRawBytes(); + } else { + final UIHHContainer uihhContainer = UIHHContainer.fromRawBytes(fileBytes); + if (uihhContainer != null) { + return fileBytes; + } } - return uihh.toRawBytes(); + throw new IllegalStateException("Unknown file bytes - this should never happen"); } } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainerTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainerTest.java new file mode 100644 index 0000000000..6e1bc351a5 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/UIHHContainerTest.java @@ -0,0 +1,19 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; + +import org.junit.Ignore; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import nodomain.freeyourgadget.gadgetbridge.test.TestBase; + +public class UIHHContainerTest extends TestBase { + @Test + @Ignore("helper test for development, remove this while debugging") + public void localTest() throws IOException { + byte[] bytes = Files.readAllBytes(new File("/storage/downloads/uihh.bin").toPath()); + final UIHHContainer uihhContainer = UIHHContainer.fromRawBytes(bytes); + } +}