PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java

https://codeberg.org/cs8898/Gadgetbridge
Java | 243 lines | 159 code | 53 blank | 31 comment | 27 complexity | 7843929515818d4f6338127bbf31d8fb MD5 | raw file
Possible License(s): AGPL-3.0
  1. /* Copyright (C) 2019-2020 Andreas Shimokawa, Daniel Dakhno
  2. This file is part of Gadgetbridge.
  3. Gadgetbridge is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as published
  5. by the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. Gadgetbridge is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file;
  14. import android.bluetooth.BluetoothGattCharacteristic;
  15. import java.nio.ByteBuffer;
  16. import java.nio.ByteOrder;
  17. import java.util.ArrayList;
  18. import java.util.UUID;
  19. import java.util.zip.CRC32;
  20. import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
  21. import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
  22. import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
  23. import nodomain.freeyourgadget.gadgetbridge.util.CRC32C;
  24. public class FilePutRequest extends FossilRequest {
  25. public enum UploadState {INITIALIZED, UPLOADING, CLOSING, UPLOADED}
  26. public UploadState state;
  27. private ArrayList<byte[]> packets = new ArrayList<>();
  28. private short handle;
  29. private FossilWatchAdapter adapter;
  30. private byte[] file;
  31. private int fullCRC;
  32. public FilePutRequest(short handle, byte[] file, FossilWatchAdapter adapter) {
  33. this.handle = handle;
  34. this.adapter = adapter;
  35. int fileLength = file.length + 16;
  36. ByteBuffer buffer = this.createBuffer();
  37. buffer.putShort(1, handle);
  38. buffer.putInt(3, 0);
  39. buffer.putInt(7, fileLength);
  40. buffer.putInt(11, fileLength);
  41. this.data = buffer.array();
  42. this.file = file;
  43. state = UploadState.INITIALIZED;
  44. }
  45. public short getHandle() {
  46. return handle;
  47. }
  48. @Override
  49. public void handleResponse(BluetoothGattCharacteristic characteristic) {
  50. byte[] value = characteristic.getValue();
  51. if (characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")) {
  52. int responseType = value[0] & 0x0F;
  53. log("response: " + responseType);
  54. switch (responseType) {
  55. case 3: {
  56. if (value.length != 5 || (value[0] & 0x0F) != 3) {
  57. throw new RuntimeException("wrong answer header");
  58. }
  59. state = UploadState.UPLOADING;
  60. TransactionBuilder transactionBuilder = new TransactionBuilder("file upload");
  61. BluetoothGattCharacteristic uploadCharacteristic = adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0004-957f-7d4a-34a6-74696673696d"));
  62. this.prepareFilePackets(this.file);
  63. for (byte[] packet : packets) {
  64. transactionBuilder.write(uploadCharacteristic, packet);
  65. }
  66. transactionBuilder.queue(adapter.getDeviceSupport().getQueue());
  67. break;
  68. }
  69. case 8: {
  70. if (value.length == 4) return;
  71. ByteBuffer buffer = ByteBuffer.wrap(value);
  72. buffer.order(ByteOrder.LITTLE_ENDIAN);
  73. short handle = buffer.getShort(1);
  74. int crc = buffer.getInt(8);
  75. byte status = value[3];
  76. if (status != 0) {
  77. throw new RuntimeException("upload status: " + status);
  78. }
  79. if (handle != this.handle) {
  80. throw new RuntimeException("wrong response handle");
  81. }
  82. if (crc != this.fullCRC) {
  83. throw new RuntimeException("file upload exception: wrong crc");
  84. }
  85. ByteBuffer buffer2 = ByteBuffer.allocate(3);
  86. buffer2.order(ByteOrder.LITTLE_ENDIAN);
  87. buffer2.put((byte) 4);
  88. buffer2.putShort(this.handle);
  89. new TransactionBuilder("file close")
  90. .write(
  91. adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")),
  92. buffer2.array()
  93. )
  94. .queue(adapter.getDeviceSupport().getQueue());
  95. this.state = UploadState.CLOSING;
  96. break;
  97. }
  98. case 4: {
  99. if (value.length == 9) return;
  100. if (value.length != 4 || (value[0] & 0x0F) != 4) {
  101. throw new RuntimeException("wrong file closing header");
  102. }
  103. ByteBuffer buffer = ByteBuffer.wrap(value);
  104. buffer.order(ByteOrder.LITTLE_ENDIAN);
  105. short handle = buffer.getShort(1);
  106. if (handle != this.handle) {
  107. onFilePut(false);
  108. throw new RuntimeException("wrong file closing handle");
  109. }
  110. byte status = buffer.get(3);
  111. if (status != 0) {
  112. onFilePut(false);
  113. throw new RuntimeException("wrong closing status: " + status);
  114. }
  115. this.state = UploadState.UPLOADED;
  116. onFilePut(true);
  117. log("uploaded file");
  118. break;
  119. }
  120. case 9: {
  121. this.onFilePut(false);
  122. throw new RuntimeException("file put timeout");
  123. /*timeout = true;
  124. ByteBuffer buffer2 = ByteBuffer.allocate(3);
  125. buffer2.order(ByteOrder.LITTLE_ENDIAN);
  126. buffer2.put((byte) 4);
  127. buffer2.putShort(this.handle);
  128. new TransactionBuilder("file close")
  129. .write(
  130. adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")),
  131. buffer2.array()
  132. )
  133. .queue(adapter.getDeviceSupport().getQueue());
  134. this.state = UploadState.CLOSING;
  135. break;*/
  136. }
  137. }
  138. }
  139. }
  140. @Override
  141. public boolean isFinished() {
  142. return this.state == UploadState.UPLOADED;
  143. }
  144. private void prepareFilePackets(byte[] file) {
  145. int maxPacketSize = adapter.getMTU() - 4;
  146. ByteBuffer buffer = ByteBuffer.allocate(file.length + 12 + 4);
  147. buffer.order(ByteOrder.LITTLE_ENDIAN);
  148. buffer.putShort(handle);
  149. buffer.put((byte) 2);
  150. buffer.put((byte) 0);
  151. buffer.putInt(0);
  152. buffer.putInt(file.length);
  153. buffer.put(file);
  154. CRC32C crc = new CRC32C();
  155. crc.update(file,0,file.length);
  156. buffer.putInt((int) crc.getValue());
  157. byte[] data = buffer.array();
  158. CRC32 fullCRC = new CRC32();
  159. fullCRC.update(data);
  160. this.fullCRC = (int) fullCRC.getValue();
  161. int packetCount = (int) Math.ceil(data.length / (float) maxPacketSize);
  162. for (int i = 0; i < packetCount; i++) {
  163. int currentPacketLength = Math.min(maxPacketSize, data.length - i * maxPacketSize);
  164. byte[] packet = new byte[currentPacketLength + 1];
  165. packet[0] = (byte) i;
  166. System.arraycopy(data, i * maxPacketSize, packet, 1, currentPacketLength);
  167. packets.add(packet);
  168. }
  169. }
  170. public void onFilePut(boolean success) {
  171. }
  172. @Override
  173. public byte[] getStartSequence() {
  174. return new byte[]{0x03};
  175. }
  176. @Override
  177. public int getPayloadLength() {
  178. return 15;
  179. }
  180. @Override
  181. public UUID getRequestUUID() {
  182. return UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d");
  183. }
  184. }