PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://codeberg.org/vanous/Gadgetbridge
Java | 246 lines | 162 code | 53 blank | 31 comment | 25 complexity | 29711a9a8f186c199b2628b310b5c1e9 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.service.devices.qhybrid.requests.fossil_hr.file.ResultCode;
  24. import nodomain.freeyourgadget.gadgetbridge.util.CRC32C;
  25. public class FilePutRequest extends FossilRequest {
  26. public enum UploadState {INITIALIZED, UPLOADING, CLOSING, UPLOADED}
  27. public UploadState state;
  28. private ArrayList<byte[]> packets = new ArrayList<>();
  29. private short handle;
  30. private FossilWatchAdapter adapter;
  31. private byte[] file;
  32. private int fullCRC;
  33. public FilePutRequest(short handle, byte[] file, FossilWatchAdapter adapter) {
  34. this.handle = handle;
  35. this.adapter = adapter;
  36. int fileLength = file.length + 16;
  37. ByteBuffer buffer = this.createBuffer();
  38. buffer.putShort(1, handle);
  39. buffer.putInt(3, 0);
  40. buffer.putInt(7, fileLength);
  41. buffer.putInt(11, fileLength);
  42. this.data = buffer.array();
  43. this.file = file;
  44. state = UploadState.INITIALIZED;
  45. }
  46. public short getHandle() {
  47. return handle;
  48. }
  49. @Override
  50. public void handleResponse(BluetoothGattCharacteristic characteristic) {
  51. byte[] value = characteristic.getValue();
  52. if (characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")) {
  53. int responseType = value[0] & 0x0F;
  54. log("response: " + responseType);
  55. switch (responseType) {
  56. case 3: {
  57. if (value.length != 5 || (value[0] & 0x0F) != 3) {
  58. throw new RuntimeException("wrong answer header");
  59. }
  60. state = UploadState.UPLOADING;
  61. TransactionBuilder transactionBuilder = new TransactionBuilder("file upload");
  62. BluetoothGattCharacteristic uploadCharacteristic = adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0004-957f-7d4a-34a6-74696673696d"));
  63. this.prepareFilePackets(this.file);
  64. for (byte[] packet : packets) {
  65. transactionBuilder.write(uploadCharacteristic, packet);
  66. }
  67. transactionBuilder.queue(adapter.getDeviceSupport().getQueue());
  68. break;
  69. }
  70. case 8: {
  71. if (value.length == 4) return;
  72. ByteBuffer buffer = ByteBuffer.wrap(value);
  73. buffer.order(ByteOrder.LITTLE_ENDIAN);
  74. short handle = buffer.getShort(1);
  75. int crc = buffer.getInt(8);
  76. byte status = value[3];
  77. ResultCode code = ResultCode.fromCode(status);
  78. if(!code.inidicatesSuccess()){
  79. throw new RuntimeException("upload status: " + code + " (" + status + ")");
  80. }
  81. if (handle != this.handle) {
  82. throw new RuntimeException("wrong response handle");
  83. }
  84. if (crc != this.fullCRC) {
  85. throw new RuntimeException("file upload exception: wrong crc");
  86. }
  87. ByteBuffer buffer2 = ByteBuffer.allocate(3);
  88. buffer2.order(ByteOrder.LITTLE_ENDIAN);
  89. buffer2.put((byte) 4);
  90. buffer2.putShort(this.handle);
  91. new TransactionBuilder("file close")
  92. .write(
  93. adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")),
  94. buffer2.array()
  95. )
  96. .queue(adapter.getDeviceSupport().getQueue());
  97. this.state = UploadState.CLOSING;
  98. break;
  99. }
  100. case 4: {
  101. if (value.length == 9) return;
  102. if (value.length != 4 || (value[0] & 0x0F) != 4) {
  103. throw new RuntimeException("wrong file closing header");
  104. }
  105. ByteBuffer buffer = ByteBuffer.wrap(value);
  106. buffer.order(ByteOrder.LITTLE_ENDIAN);
  107. short handle = buffer.getShort(1);
  108. if (handle != this.handle) {
  109. onFilePut(false);
  110. throw new RuntimeException("wrong file closing handle");
  111. }
  112. byte status = buffer.get(3);
  113. ResultCode code = ResultCode.fromCode(status);
  114. if(!code.inidicatesSuccess()){
  115. onFilePut(false);
  116. throw new RuntimeException("wrong closing status: " + code + " (" + status + ")");
  117. }
  118. this.state = UploadState.UPLOADED;
  119. onFilePut(true);
  120. log("uploaded file");
  121. break;
  122. }
  123. case 9: {
  124. this.onFilePut(false);
  125. throw new RuntimeException("file put timeout");
  126. /*timeout = true;
  127. ByteBuffer buffer2 = ByteBuffer.allocate(3);
  128. buffer2.order(ByteOrder.LITTLE_ENDIAN);
  129. buffer2.put((byte) 4);
  130. buffer2.putShort(this.handle);
  131. new TransactionBuilder("file close")
  132. .write(
  133. adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")),
  134. buffer2.array()
  135. )
  136. .queue(adapter.getDeviceSupport().getQueue());
  137. this.state = UploadState.CLOSING;
  138. break;*/
  139. }
  140. }
  141. }
  142. }
  143. @Override
  144. public boolean isFinished() {
  145. return this.state == UploadState.UPLOADED;
  146. }
  147. private void prepareFilePackets(byte[] file) {
  148. int maxPacketSize = adapter.getMTU() - 4;
  149. ByteBuffer buffer = ByteBuffer.allocate(file.length + 12 + 4);
  150. buffer.order(ByteOrder.LITTLE_ENDIAN);
  151. buffer.putShort(handle);
  152. buffer.put((byte) 2);
  153. buffer.put((byte) 0);
  154. buffer.putInt(0);
  155. buffer.putInt(file.length);
  156. buffer.put(file);
  157. CRC32C crc = new CRC32C();
  158. crc.update(file,0,file.length);
  159. buffer.putInt((int) crc.getValue());
  160. byte[] data = buffer.array();
  161. CRC32 fullCRC = new CRC32();
  162. fullCRC.update(data);
  163. this.fullCRC = (int) fullCRC.getValue();
  164. int packetCount = (int) Math.ceil(data.length / (float) maxPacketSize);
  165. for (int i = 0; i < packetCount; i++) {
  166. int currentPacketLength = Math.min(maxPacketSize, data.length - i * maxPacketSize);
  167. byte[] packet = new byte[currentPacketLength + 1];
  168. packet[0] = (byte) i;
  169. System.arraycopy(data, i * maxPacketSize, packet, 1, currentPacketLength);
  170. packets.add(packet);
  171. }
  172. }
  173. public void onFilePut(boolean success) {
  174. }
  175. @Override
  176. public byte[] getStartSequence() {
  177. return new byte[]{0x03};
  178. }
  179. @Override
  180. public int getPayloadLength() {
  181. return 15;
  182. }
  183. @Override
  184. public UUID getRequestUUID() {
  185. return UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d");
  186. }
  187. }