/* Copyright (C) 2019-2024 Andreas Shimokawa, Daniel Dakhno, Taavi Eomäe This file is part of Gadgetbridge. Gadgetbridge is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gadgetbridge is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit; import android.bluetooth.BluetoothGattCharacteristic; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.zip.CRC32; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode; public class DownloadFileRequest extends FileRequest { ByteBuffer buffer = null; public byte[] file = null; public int fileHandle; public int size; public long timeStamp; public DownloadFileRequest(short handle){ init(handle, 0, 65535); } public DownloadFileRequest(short handle, int offset, int length) { init(handle, offset, length); } private void init(short handle, int offset, int length) { ByteBuffer buffer = createBuffer(); buffer.putShort(handle); buffer.putInt(offset); buffer.putInt(length); this.data = buffer.array(); this.fileHandle = handle; this.timeStamp = System.currentTimeMillis(); } @Override public byte[] getStartSequence() { return new byte[]{1}; } @Override public int getPayloadLength() { return 11; } @Override public void handleResponse(BluetoothGattCharacteristic characteristic) { super.handleResponse(characteristic); byte[] data = characteristic.getValue(); if(characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")){ if(buffer == null){ buffer = ByteBuffer.allocate(4096); ByteBuffer buffer1 = ByteBuffer.wrap(data); buffer1.order(ByteOrder.LITTLE_ENDIAN); this.status = buffer1.get(3); short realHandle = buffer1.getShort(1); ResultCode code = ResultCode.fromCode(status); if(!code.inidicatesSuccess()){ log("wrong status: " + code + " (" + status + ")"); }else if(realHandle != fileHandle){ log("wrong handle: " + realHandle); completed = true; }else{ log("handle: " + realHandle); } }else{ completed = true; } }else if(characteristic.getUuid().toString().equals("3dda0004-957f-7d4a-34a6-74696673696d")){ buffer.put(data, 1, data.length - 1); if((data[0] & -128) != 0){ ByteBuffer buffer1 = ByteBuffer.allocate(buffer.position()); buffer1.put(buffer.array(), 0, buffer.position()); buffer1.order(ByteOrder.LITTLE_ENDIAN); file = buffer1.array(); CRC32 crc = new CRC32(); crc.update(file, 0, file.length - 4); this.size = file.length; log("file content: " + bytesToString(file)); if(crc.getValue() != cutBits(buffer1.getInt(size - 4))){ log("checksum invalid expected: " + buffer1.getInt(size - 4) + " actual: " + crc.getValue()); } } } } long cutBits(int value) { return value & 0b11111111111111111111111111111111L; } private String bytesToString(byte[] bytes){ StringBuilder s = new StringBuilder(); String chars = "0123456789ABCDEF"; for (byte b : bytes) { s.append(chars.charAt((b >> 4) & 0xF)); s.append(chars.charAt((b >> 0) & 0xF)); } return s.toString(); } }