@glassly/bluetooth-sdk 0.1.0-dev.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. package/README.md +563 -0
  2. package/android/build.gradle +450 -0
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/CMakeLists.txt +6 -0
  5. package/android/lc3Lib/build.gradle +212 -0
  6. package/android/lc3Lib/proguard-rules.pro +33 -0
  7. package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
  8. package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
  9. package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
  10. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
  11. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
  12. package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
  13. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
  14. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
  15. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
  16. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
  17. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
  18. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
  19. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
  20. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
  21. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
  22. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
  23. package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
  24. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
  25. package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
  26. package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
  27. package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
  28. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
  29. package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
  30. package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
  31. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
  32. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
  33. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
  34. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
  35. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
  36. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
  37. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
  38. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
  39. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
  40. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
  41. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
  42. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
  43. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
  44. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
  45. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
  46. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
  47. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
  48. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
  49. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
  50. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
  51. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
  52. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
  53. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
  54. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
  55. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
  56. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
  57. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
  58. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
  59. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
  60. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
  61. package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
  62. package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
  63. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
  64. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
  65. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
  66. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
  67. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
  68. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
  69. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
  70. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
  71. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
  72. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
  73. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
  74. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
  75. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
  76. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
  77. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
  78. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
  79. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
  80. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
  81. package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
  82. package/android/lc3Lib/src/main/java/com/glassly/lc3Lib/Lc3Cpp.java +38 -0
  83. package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
  84. package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
  85. package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
  86. package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
  87. package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
  88. package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
  89. package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
  90. package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
  91. package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
  92. package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
  93. package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
  94. package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
  95. package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
  96. package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
  97. package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
  98. package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
  99. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
  100. package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
  101. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
  102. package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
  103. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
  104. package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
  105. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
  106. package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
  107. package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
  108. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
  109. package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
  110. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
  111. package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
  112. package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
  113. package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
  114. package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
  115. package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
  116. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
  117. package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
  118. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
  119. package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
  120. package/android/settings.gradle +4 -0
  121. package/android/silero/LICENSE.md +17 -0
  122. package/android/silero/build.gradle +50 -0
  123. package/android/silero/proguard-rules.pro +24 -0
  124. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  125. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  126. package/android/silero/src/main/assets/LICENSE +21 -0
  127. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  128. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  129. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  130. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  131. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  132. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  133. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  134. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  135. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  136. package/android/src/main/AndroidManifest.xml +42 -0
  137. package/android/src/main/assets/G2Firmware/README.md +11 -0
  138. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2325 -0
  139. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
  140. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
  141. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
  142. package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1088 -0
  143. package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
  144. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2528 -0
  145. package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +360 -0
  146. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +8 -0
  147. package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
  148. package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
  149. package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
  150. package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
  151. package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
  152. package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
  153. package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
  154. package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
  155. package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  156. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
  157. package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +937 -0
  158. package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
  159. package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
  160. package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
  161. package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
  162. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
  163. package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
  164. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  165. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
  166. package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
  167. package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
  168. package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
  169. package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
  170. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
  171. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
  172. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
  173. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3878 -0
  174. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
  175. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6428 -0
  176. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
  177. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1241 -0
  178. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
  179. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
  180. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
  181. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
  182. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1280 -0
  183. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10817 -0
  184. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
  185. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
  186. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2011 -0
  187. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  188. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
  189. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
  190. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
  191. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
  192. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
  193. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
  194. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
  195. package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
  196. package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
  197. package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
  198. package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
  199. package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
  200. package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
  201. package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
  202. package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
  203. package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
  204. package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
  205. package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
  206. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
  207. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
  208. package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  209. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
  210. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
  211. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
  212. package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
  213. package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
  214. package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
  215. package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  216. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
  217. package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
  218. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
  219. package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
  220. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
  221. package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
  222. package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
  223. package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
  224. package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
  225. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
  226. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
  227. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
  228. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
  229. package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
  230. package/android/src/main/res/values/strings.xml +4 -0
  231. package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
  232. package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
  233. package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
  234. package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
  235. package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
  236. package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
  237. package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
  238. package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
  239. package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  240. package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
  241. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
  242. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
  243. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
  244. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
  245. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
  246. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
  247. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
  248. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
  249. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
  250. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
  251. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
  252. package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  253. package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
  254. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
  255. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
  256. package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
  257. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
  258. package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
  259. package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
  260. package/android/src/test/resources/avif_with_exif.avif +0 -0
  261. package/app.plugin.js +3 -0
  262. package/build/BluetoothSdk.types.d.ts +1354 -0
  263. package/build/BluetoothSdk.types.d.ts.map +1 -0
  264. package/build/BluetoothSdk.types.js +41 -0
  265. package/build/BluetoothSdk.types.js.map +1 -0
  266. package/build/_internal.d.ts +17 -0
  267. package/build/_internal.d.ts.map +1 -0
  268. package/build/_internal.js +16 -0
  269. package/build/_internal.js.map +1 -0
  270. package/build/_private/BluetoothSdkModule.d.ts +172 -0
  271. package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
  272. package/build/_private/BluetoothSdkModule.js +300 -0
  273. package/build/_private/BluetoothSdkModule.js.map +1 -0
  274. package/build/_private/LocalNetworkModule.d.ts +38 -0
  275. package/build/_private/LocalNetworkModule.d.ts.map +1 -0
  276. package/build/_private/LocalNetworkModule.js +3 -0
  277. package/build/_private/LocalNetworkModule.js.map +1 -0
  278. package/build/_private/cameraRequestPayload.d.ts +4 -0
  279. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  280. package/build/_private/cameraRequestPayload.js +32 -0
  281. package/build/_private/cameraRequestPayload.js.map +1 -0
  282. package/build/_private/photoRequestPayload.d.ts +6 -0
  283. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  284. package/build/_private/photoRequestPayload.js +86 -0
  285. package/build/_private/photoRequestPayload.js.map +1 -0
  286. package/build/_private/sdkOtaManifest.d.ts +11 -0
  287. package/build/_private/sdkOtaManifest.d.ts.map +1 -0
  288. package/build/_private/sdkOtaManifest.js +15 -0
  289. package/build/_private/sdkOtaManifest.js.map +1 -0
  290. package/build/changelogs.d.ts +7 -0
  291. package/build/changelogs.d.ts.map +1 -0
  292. package/build/changelogs.js +46 -0
  293. package/build/changelogs.js.map +1 -0
  294. package/build/debug.d.ts +3 -0
  295. package/build/debug.d.ts.map +1 -0
  296. package/build/debug.js +8 -0
  297. package/build/debug.js.map +1 -0
  298. package/build/generated/changelogCatalog.d.ts +9 -0
  299. package/build/generated/changelogCatalog.d.ts.map +1 -0
  300. package/build/generated/changelogCatalog.js +12 -0
  301. package/build/generated/changelogCatalog.js.map +1 -0
  302. package/build/generated/releaseMetadata.d.ts +12 -0
  303. package/build/generated/releaseMetadata.d.ts.map +1 -0
  304. package/build/generated/releaseMetadata.js +10 -0
  305. package/build/generated/releaseMetadata.js.map +1 -0
  306. package/build/index.d.ts +6 -0
  307. package/build/index.d.ts.map +1 -0
  308. package/build/index.js +171 -0
  309. package/build/index.js.map +1 -0
  310. package/build/ota-server/OtaServer.types.d.ts +14 -0
  311. package/build/ota-server/OtaServer.types.d.ts.map +1 -0
  312. package/build/ota-server/OtaServer.types.js +2 -0
  313. package/build/ota-server/OtaServer.types.js.map +1 -0
  314. package/build/ota-server/OtaServerModule.d.ts +21 -0
  315. package/build/ota-server/OtaServerModule.d.ts.map +1 -0
  316. package/build/ota-server/OtaServerModule.js +3 -0
  317. package/build/ota-server/OtaServerModule.js.map +1 -0
  318. package/build/ota-server/OtaServerModule.web.d.ts +10 -0
  319. package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
  320. package/build/ota-server/OtaServerModule.web.js +12 -0
  321. package/build/ota-server/OtaServerModule.web.js.map +1 -0
  322. package/build/ota-server/index.d.ts +3 -0
  323. package/build/ota-server/index.d.ts.map +1 -0
  324. package/build/ota-server/index.js +3 -0
  325. package/build/ota-server/index.js.map +1 -0
  326. package/build/ota-transport/index.d.ts +67 -0
  327. package/build/ota-transport/index.d.ts.map +1 -0
  328. package/build/ota-transport/index.js +58 -0
  329. package/build/ota-transport/index.js.map +1 -0
  330. package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
  331. package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
  332. package/build/photo-receiver/PhotoReceiver.types.js +2 -0
  333. package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
  334. package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
  335. package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
  336. package/build/photo-receiver/PhotoReceiverModule.js +3 -0
  337. package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
  338. package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
  339. package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
  340. package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
  341. package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
  342. package/build/photo-receiver/index.d.ts +3 -0
  343. package/build/photo-receiver/index.d.ts.map +1 -0
  344. package/build/photo-receiver/index.js +3 -0
  345. package/build/photo-receiver/index.js.map +1 -0
  346. package/build/react/index.d.ts +5 -0
  347. package/build/react/index.d.ts.map +1 -0
  348. package/build/react/index.js +4 -0
  349. package/build/react/index.js.map +1 -0
  350. package/build/react/useBluetooth.d.ts +100 -0
  351. package/build/react/useBluetooth.d.ts.map +1 -0
  352. package/build/react/useBluetooth.js +167 -0
  353. package/build/react/useBluetooth.js.map +1 -0
  354. package/build/react/useBluetoothEvent.d.ts +6 -0
  355. package/build/react/useBluetoothEvent.d.ts.map +1 -0
  356. package/build/react/useBluetoothEvent.js +21 -0
  357. package/build/react/useBluetoothEvent.js.map +1 -0
  358. package/build/react/useBluetoothScan.d.ts +22 -0
  359. package/build/react/useBluetoothScan.d.ts.map +1 -0
  360. package/build/react/useBluetoothScan.js +135 -0
  361. package/build/react/useBluetoothScan.js.map +1 -0
  362. package/build/react/useBluetoothStatus.d.ts +16 -0
  363. package/build/react/useBluetoothStatus.d.ts.map +1 -0
  364. package/build/react/useBluetoothStatus.js +137 -0
  365. package/build/react/useBluetoothStatus.js.map +1 -0
  366. package/build/react/useGlassesConnection.d.ts +29 -0
  367. package/build/react/useGlassesConnection.d.ts.map +1 -0
  368. package/build/react/useGlassesConnection.js +153 -0
  369. package/build/react/useGlassesConnection.js.map +1 -0
  370. package/build/types/index.d.ts +3 -0
  371. package/build/types/index.d.ts.map +1 -0
  372. package/build/types/index.js +2 -0
  373. package/build/types/index.js.map +1 -0
  374. package/expo-module.config.json +14 -0
  375. package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
  376. package/ios/BluetoothSdkModule.swift +1034 -0
  377. package/ios/GlasslyBluetoothSDK.podspec +106 -0
  378. package/ios/LocalIPv4.swift +150 -0
  379. package/ios/LocalOtaServer.swift +309 -0
  380. package/ios/LocalPhotoUploadServer.swift +754 -0
  381. package/ios/OtaServerModule.swift +147 -0
  382. package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
  383. package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
  384. package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
  385. package/ios/Packages/CoreObjC/attdet.c +92 -0
  386. package/ios/Packages/CoreObjC/attdet.h +44 -0
  387. package/ios/Packages/CoreObjC/bits.c +375 -0
  388. package/ios/Packages/CoreObjC/bits.h +315 -0
  389. package/ios/Packages/CoreObjC/bwdet.c +129 -0
  390. package/ios/Packages/CoreObjC/bwdet.h +69 -0
  391. package/ios/Packages/CoreObjC/common.h +151 -0
  392. package/ios/Packages/CoreObjC/energy.c +70 -0
  393. package/ios/Packages/CoreObjC/energy.h +43 -0
  394. package/ios/Packages/CoreObjC/fastmath.h +158 -0
  395. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  396. package/ios/Packages/CoreObjC/lc3.c +704 -0
  397. package/ios/Packages/CoreObjC/lc3.h +313 -0
  398. package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
  399. package/ios/Packages/CoreObjC/lc3_private.h +163 -0
  400. package/ios/Packages/CoreObjC/ltpf.c +905 -0
  401. package/ios/Packages/CoreObjC/ltpf.h +111 -0
  402. package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
  403. package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
  404. package/ios/Packages/CoreObjC/makefile.mk +35 -0
  405. package/ios/Packages/CoreObjC/mdct.c +469 -0
  406. package/ios/Packages/CoreObjC/mdct.h +57 -0
  407. package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
  408. package/ios/Packages/CoreObjC/meson.build +61 -0
  409. package/ios/Packages/CoreObjC/plc.c +61 -0
  410. package/ios/Packages/CoreObjC/plc.h +57 -0
  411. package/ios/Packages/CoreObjC/rnnoise.h +114 -0
  412. package/ios/Packages/CoreObjC/sns.c +880 -0
  413. package/ios/Packages/CoreObjC/sns.h +103 -0
  414. package/ios/Packages/CoreObjC/spec.c +907 -0
  415. package/ios/Packages/CoreObjC/spec.h +119 -0
  416. package/ios/Packages/CoreObjC/tables.c +3457 -0
  417. package/ios/Packages/CoreObjC/tables.h +94 -0
  418. package/ios/Packages/CoreObjC/tns.c +457 -0
  419. package/ios/Packages/CoreObjC/tns.h +99 -0
  420. package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
  421. package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
  422. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
  423. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
  424. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  425. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  426. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  427. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
  428. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
  429. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  430. package/ios/Packages/libbz2/module.modulemap +5 -0
  431. package/ios/Packages/libbz2/shim.h +1 -0
  432. package/ios/PhotoReceiverModule.swift +129 -0
  433. package/ios/Source/BluetoothSDK.swift +2303 -0
  434. package/ios/Source/BluetoothSDKDebug.swift +12 -0
  435. package/ios/Source/BluetoothSdkDefaults.swift +29 -0
  436. package/ios/Source/Bridge.swift +708 -0
  437. package/ios/Source/Bridging-Header.h +16 -0
  438. package/ios/Source/DeviceManager.swift +2109 -0
  439. package/ios/Source/DeviceStore.swift +336 -0
  440. package/ios/Source/GeneratedChangelogCatalog.swift +7 -0
  441. package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
  442. package/ios/Source/ObservableStore.swift +118 -0
  443. package/ios/Source/OtaManifest.swift +211 -0
  444. package/ios/Source/PrivacyInfo.xcprivacy +23 -0
  445. package/ios/Source/ReleaseChangelog.swift +65 -0
  446. package/ios/Source/Resources/G2Firmware/README.md +10 -0
  447. package/ios/Source/audio/AudioModels.swift +178 -0
  448. package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
  449. package/ios/Source/camera/CameraModels.swift +866 -0
  450. package/ios/Source/connection/ScanSession.swift +27 -0
  451. package/ios/Source/controllers/ControllerManager.swift +184 -0
  452. package/ios/Source/controllers/R1.swift +829 -0
  453. package/ios/Source/errors/BluetoothSdkError.swift +19 -0
  454. package/ios/Source/events/BluetoothEvents.swift +342 -0
  455. package/ios/Source/internal/BleTraceLogger.swift +192 -0
  456. package/ios/Source/internal/BluetoothAvailability.swift +78 -0
  457. package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
  458. package/ios/Source/internal/ValueParsing.swift +99 -0
  459. package/ios/Source/requests/DisplayRequests.swift +82 -0
  460. package/ios/Source/services/PhoneMic.swift +662 -0
  461. package/ios/Source/sgcs/Ar99.swift +2451 -0
  462. package/ios/Source/sgcs/Frame.swift +649 -0
  463. package/ios/Source/sgcs/G1.swift +2554 -0
  464. package/ios/Source/sgcs/G2.swift +6887 -0
  465. package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
  466. package/ios/Source/sgcs/G2CfwRenderer.swift +1279 -0
  467. package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
  468. package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
  469. package/ios/Source/sgcs/G2Flasher.swift +773 -0
  470. package/ios/Source/sgcs/Mach1.swift +541 -0
  471. package/ios/Source/sgcs/MentraLive.swift +7011 -0
  472. package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
  473. package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
  474. package/ios/Source/sgcs/MentraNex.swift +3221 -0
  475. package/ios/Source/sgcs/Nimo.swift +1886 -0
  476. package/ios/Source/sgcs/SGCManager.swift +608 -0
  477. package/ios/Source/sgcs/Simulated.swift +274 -0
  478. package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
  479. package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
  480. package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
  481. package/ios/Source/status/DeviceStatus.swift +981 -0
  482. package/ios/Source/status/RuntimeState.swift +359 -0
  483. package/ios/Source/status/WifiHotspotStatus.swift +345 -0
  484. package/ios/Source/streaming/StreamModels.swift +693 -0
  485. package/ios/Source/stt/STTTools.swift +191 -0
  486. package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
  487. package/ios/Source/tts/TTSTools.swift +171 -0
  488. package/ios/Source/types/DeviceModels.swift +177 -0
  489. package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
  490. package/ios/Source/utils/BinaryCodec.swift +61 -0
  491. package/ios/Source/utils/BleJsonCompact.swift +405 -0
  492. package/ios/Source/utils/BleWireProtocol.swift +92 -0
  493. package/ios/Source/utils/Constants.swift +78 -0
  494. package/ios/Source/utils/Enums.swift +95 -0
  495. package/ios/Source/utils/G1Text.swift +2096 -0
  496. package/ios/Source/utils/JSCExperiment.swift +241 -0
  497. package/ios/Source/utils/MemoryMonitor.swift +44 -0
  498. package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
  499. package/ios/Source/utils/MessageChunker.swift +294 -0
  500. package/ios/Source/utils/Models.swift +135 -0
  501. package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
  502. package/ios/Source/utils/TarBz2Extractor.swift +257 -0
  503. package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
  504. package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
  505. package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
  506. package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
  507. package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
  508. package/ios/Tests/ReleaseChangelogTests.swift +13 -0
  509. package/package.json +129 -0
  510. package/plugin/build/index.d.ts +10 -0
  511. package/plugin/build/index.js +12 -0
  512. package/plugin/build/withAndroid.d.ts +3 -0
  513. package/plugin/build/withAndroid.js +190 -0
  514. package/plugin/build/withIos.d.ts +5 -0
  515. package/plugin/build/withIos.js +108 -0
  516. package/scripts/inject-ios-sdk-version.mjs +65 -0
  517. package/scripts/public-ota-api.test.mjs +37 -0
  518. package/scripts/verify-release-package.mjs +45 -0
  519. package/scripts/write-release-metadata.mjs +152 -0
  520. package/scripts/write-release-metadata.test.mjs +37 -0
  521. package/src/BluetoothSdk.types.ts +1623 -0
  522. package/src/_internal.ts +16 -0
  523. package/src/_private/BluetoothSdkModule.ts +684 -0
  524. package/src/_private/LocalNetworkModule.ts +50 -0
  525. package/src/_private/cameraRequestPayload.ts +34 -0
  526. package/src/_private/photoRequestPayload.ts +90 -0
  527. package/src/_private/sdkOtaManifest.ts +18 -0
  528. package/src/changelogs.ts +46 -0
  529. package/src/debug.ts +9 -0
  530. package/src/generated/changelogCatalog.ts +13 -0
  531. package/src/generated/releaseMetadata.ts +20 -0
  532. package/src/index.ts +321 -0
  533. package/src/ota-server/OtaServer.types.ts +10 -0
  534. package/src/ota-server/OtaServerModule.ts +23 -0
  535. package/src/ota-server/OtaServerModule.web.ts +17 -0
  536. package/src/ota-server/index.ts +2 -0
  537. package/src/ota-transport/index.ts +116 -0
  538. package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
  539. package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
  540. package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
  541. package/src/photo-receiver/index.ts +2 -0
  542. package/src/react/index.ts +21 -0
  543. package/src/react/useBluetooth.ts +309 -0
  544. package/src/react/useBluetoothEvent.ts +35 -0
  545. package/src/react/useBluetoothScan.ts +173 -0
  546. package/src/react/useBluetoothStatus.ts +180 -0
  547. package/src/react/useGlassesConnection.ts +202 -0
  548. package/src/types/index.ts +12 -0
@@ -0,0 +1,233 @@
1
+ import Foundation
2
+
3
+ /// Phone-side model of the Glassly CFW's lease-scoped 64 KiB texture cache,
4
+ /// plus the mode-12/13/14 wire encoders.
5
+ ///
6
+ /// The firmware allocates and zeroes the cache on the first mode-12 write and
7
+ /// frees it when the fbguard lease ends, so this model is only valid for one
8
+ /// lease/generation: the owner bumps a generation counter whenever the lease
9
+ /// cycles (or a send fails and residency becomes unknown) and the model
10
+ /// re-learns from scratch — uploads are cheap (a full mono font is a few KiB).
11
+ ///
12
+ /// Cached image format: `[width:u8][height:u8][4bpp RLE]` where the RLE covers
13
+ /// exactly width*height pixels with NO row-pad nibbles (unlike modes 3/6).
14
+ /// Mode-14 fonts are a 96-entry little-endian u16 image-offset table for
15
+ /// characters 32..127; a zero entry points at cache offset 0, so the first
16
+ /// two bytes of the cache are left zero (width 0 ⇒ invalid image) and real
17
+ /// allocations start past them.
18
+ final class G2CfwTextureCache {
19
+ static let cacheSize = 64 * 1024
20
+ static let fontTableChars = 96 // ASCII 32..127
21
+ /// Keep mode-12 payloads comfortably inside one BLE image message.
22
+ static let maxUploadPayloadBytes = 3600
23
+ /// 0x0F: identity ramp (top color 15), opaque — a full-cell glyph or a
24
+ /// full-box image drawn opaque self-erases whatever was under it.
25
+ static let opaqueDrawOptions: UInt8 = 0x0F
26
+
27
+ final class FontEntry {
28
+ let tableOffset: Int
29
+ let cellWidth: Int
30
+ let lineHeight: Int
31
+ /// Cache offset per printable char (index = char - 32), nil = not resident.
32
+ var glyphOffsets = [Int?](repeating: nil, count: G2CfwTextureCache.fontTableChars)
33
+
34
+ init(tableOffset: Int, cellWidth: Int, lineHeight: Int) {
35
+ self.tableOffset = tableOffset
36
+ self.cellWidth = cellWidth
37
+ self.lineHeight = lineHeight
38
+ }
39
+ }
40
+
41
+ private(set) var generation = -1
42
+ private var fonts: [Int32: FontEntry] = [:]
43
+ private var images: [String: Int] = [:]
44
+ /// Content hashes seen in earlier frames. Streaming content (a minimap
45
+ /// re-rendered every frame) never repeats a hash, so it never enters the
46
+ /// cache; static icons promote on their second sighting.
47
+ private var seenImageHashes: [String] = []
48
+ /// Bump allocator. Bytes 0..1 stay zero as the invalid-image guard.
49
+ private var nextOffset = 2
50
+ /// Mode-12 entries not yet flushed into payloads.
51
+ private var pendingUploads: [(offset: Int, data: Data)] = []
52
+ /// The bump allocator filled up. The reference client recycles a full
53
+ /// cache (`TextureCacheState.reset` "or we ran out of space"); without
54
+ /// recovery every later ensure fails and the texture path silently dies
55
+ /// for the rest of the lease. The renderer checks this before each frame
56
+ /// and starts a fresh generation.
57
+ private(set) var exhausted = false
58
+
59
+ /// Forget everything and adopt `generation` (the on-glass cache was freed
60
+ /// or its state became unknown).
61
+ func reset(generation: Int) {
62
+ self.generation = generation
63
+ fonts.removeAll()
64
+ images.removeAll()
65
+ seenImageHashes.removeAll()
66
+ nextOffset = 2
67
+ pendingUploads.removeAll()
68
+ exhausted = false
69
+ }
70
+
71
+ private func allocate(_ length: Int) -> Int? {
72
+ guard length > 0, nextOffset + length <= Self.cacheSize else {
73
+ if length > 0 { exhausted = true }
74
+ return nil
75
+ }
76
+ defer { nextOffset += length }
77
+ return nextOffset
78
+ }
79
+
80
+ /// The font table for `fontSize`, allocating (and zero-filling on-glass by
81
+ /// virtue of the fresh cache) the 192-byte offset table on first use.
82
+ func font(fontSize: Int32, cellWidth: Int, lineHeight: Int) -> FontEntry? {
83
+ if let existing = fonts[fontSize] { return existing }
84
+ guard let tableOffset = allocate(Self.fontTableChars * 2) else { return nil }
85
+ let entry = FontEntry(tableOffset: tableOffset, cellWidth: cellWidth, lineHeight: lineHeight)
86
+ fonts[fontSize] = entry
87
+ return entry
88
+ }
89
+
90
+ /// Make `char` (ASCII 32..126) resident for `font`, uploading its RLE
91
+ /// raster and patching the font table. `rasterize` supplies the full-cell
92
+ /// 8-bit grayscale raster (cellWidth × lineHeight) on first upload.
93
+ /// Returns false when the cache is full or the raster is unavailable.
94
+ func ensureGlyph(font: FontEntry, char: UInt8, rasterize: () -> Data?) -> Bool {
95
+ guard char >= 32, char <= 126 else { return false }
96
+ let index = Int(char) - 32
97
+ if font.glyphOffsets[index] != nil { return true }
98
+ guard let gray = rasterize(), gray.count == font.cellWidth * font.lineHeight else { return false }
99
+ var image = Data([UInt8(font.cellWidth), UInt8(font.lineHeight)])
100
+ image.append(Self.rleEncodePixels(gray))
101
+ guard let offset = allocate(image.count) else { return false }
102
+ font.glyphOffsets[index] = offset
103
+ pendingUploads.append((offset: offset, data: image))
104
+ pendingUploads.append((
105
+ offset: font.tableOffset + index * 2,
106
+ data: Data([UInt8(offset & 0xFF), UInt8((offset >> 8) & 0xFF)])
107
+ ))
108
+ return true
109
+ }
110
+
111
+ /// Record a sighting of an image content hash; true when it was already
112
+ /// seen in an earlier frame (worth caching).
113
+ func noteImageSighting(_ hash: String) -> Bool {
114
+ if images[hash] != nil { return true }
115
+ if seenImageHashes.contains(hash) { return true }
116
+ seenImageHashes.append(hash)
117
+ if seenImageHashes.count > 32 { seenImageHashes.removeFirst() }
118
+ return false
119
+ }
120
+
121
+ /// The cache offset of an image, uploading it on first residency.
122
+ /// `gray` is the width×height 8-bit grayscale raster.
123
+ func ensureImage(hash: String, gray: Data, width: Int, height: Int) -> Int? {
124
+ if let offset = images[hash] { return offset }
125
+ guard width > 0, width <= 255, height > 0, height <= 255,
126
+ gray.count == width * height
127
+ else { return nil }
128
+ var image = Data([UInt8(width), UInt8(height)])
129
+ image.append(Self.rleEncodePixels(gray))
130
+ guard let offset = allocate(image.count) else { return nil }
131
+ images[hash] = offset
132
+ pendingUploads.append((offset: offset, data: image))
133
+ return offset
134
+ }
135
+
136
+ /// Drain the pending mode-12 entries into `[12][offset16][len16][data]...`
137
+ /// payloads, each at most `maxUploadPayloadBytes`. An entry larger than
138
+ /// the budget gets a payload of its own (the BLE fragmenter splits it).
139
+ func takeUploadPayloads() -> [Data] {
140
+ guard !pendingUploads.isEmpty else { return [] }
141
+ var payloads: [Data] = []
142
+ var current = Data([12])
143
+ for entry in pendingUploads {
144
+ var record = Data()
145
+ record.append(UInt8(entry.offset & 0xFF))
146
+ record.append(UInt8((entry.offset >> 8) & 0xFF))
147
+ record.append(UInt8(entry.data.count & 0xFF))
148
+ record.append(UInt8((entry.data.count >> 8) & 0xFF))
149
+ record.append(entry.data)
150
+ if current.count > 1, current.count + record.count > Self.maxUploadPayloadBytes {
151
+ payloads.append(current)
152
+ current = Data([12])
153
+ }
154
+ current.append(record)
155
+ }
156
+ if current.count > 1 { payloads.append(current) }
157
+ pendingUploads.removeAll()
158
+ return payloads
159
+ }
160
+
161
+ // MARK: - Wire encoders
162
+
163
+ /// RLE over a flat 8-bit grayscale pixel sequence (quantized to 4bpp),
164
+ /// covering exactly the pixel count — no row padding, per the cached-image
165
+ /// contract. Token forms match modes 3/6.
166
+ static func rleEncodePixels(_ gray: Data) -> Data {
167
+ var output = Data()
168
+ let bytes = [UInt8](gray)
169
+ var index = 0
170
+ while index < bytes.count {
171
+ // Same rounded quantization as pack4Bpp: a cached draw must
172
+ // reproduce the composited frame's exact levels or the phone-side
173
+ // shadow diverges from the panel.
174
+ let color = G2CfwRenderer.grayNibble(bytes[index])
175
+ var end = index + 1
176
+ while end < bytes.count, G2CfwRenderer.grayNibble(bytes[end]) == color { end += 1 }
177
+ var remaining = end - index
178
+ while remaining > 0 {
179
+ let chunk = min(remaining, 0xFFFF)
180
+ if chunk <= 15 {
181
+ output.append(UInt8(chunk << 4) | color)
182
+ } else if chunk <= 255 {
183
+ output.append(color)
184
+ output.append(UInt8(chunk))
185
+ } else {
186
+ output.append(color)
187
+ output.append(0)
188
+ output.append(UInt8(chunk & 0xFF))
189
+ output.append(UInt8((chunk >> 8) & 0xFF))
190
+ }
191
+ remaining -= chunk
192
+ }
193
+ index = end
194
+ }
195
+ return output
196
+ }
197
+
198
+ /// Mode-13 cached-image draw: `[13][offset:u16][x:u16][y:u16][options]`.
199
+ static func imageDrawPayload(offset: Int, x: Int, y: Int, options: UInt8) -> Data? {
200
+ guard offset >= 0, offset <= 0xFFFF, x >= 0, x <= 0xFFFF, y >= 0, y <= 0xFFFF
201
+ else { return nil }
202
+ var payload = Data([13])
203
+ for value in [offset, x, y] {
204
+ payload.append(UInt8(value & 0xFF))
205
+ payload.append(UInt8((value >> 8) & 0xFF))
206
+ }
207
+ payload.append(options)
208
+ return payload
209
+ }
210
+
211
+ /// Mode-14 cached-glyph run:
212
+ /// `[14][font-offset:u16][x:u16][y:u16][options][strlen:u8][chars]`.
213
+ /// Every char must be 32..126 and resident (the firmware validates each
214
+ /// referenced glyph and rejects the whole draw otherwise).
215
+ static func stringRunPayload(
216
+ fontTableOffset: Int, x: Int, y: Int, options: UInt8, chars: [UInt8]
217
+ ) -> Data? {
218
+ guard !chars.isEmpty, chars.count <= 255,
219
+ fontTableOffset >= 0, fontTableOffset <= cacheSize - fontTableChars * 2,
220
+ x >= 0, x <= 0xFFFF, y >= 0, y <= 0xFFFF,
221
+ chars.allSatisfy({ $0 >= 32 && $0 <= 126 })
222
+ else { return nil }
223
+ var payload = Data([14])
224
+ for value in [fontTableOffset, x, y] {
225
+ payload.append(UInt8(value & 0xFF))
226
+ payload.append(UInt8((value >> 8) & 0xFF))
227
+ }
228
+ payload.append(options)
229
+ payload.append(UInt8(chars.count))
230
+ payload.append(contentsOf: chars)
231
+ return payload
232
+ }
233
+ }
@@ -0,0 +1,312 @@
1
+ //
2
+ // G2FlashProtocol.swift
3
+ // Glassly's pure Swift port of the G2 AA21/EVENOTA protocol.
4
+ //
5
+ // PURE-LOGIC layer, NO CoreBluetooth and NO writes: the byte-exact OTA framing +
6
+ // CRC16/CRC32C + EVENOTA container parsing + the MRAM brick-guard, plus a golden-vector
7
+ // self-test. The BLE flash state machine (which drives these over the link) lives in
8
+ // G2Flasher; this file is the protocol + the safety guard, deliberately isolated so it
9
+ // can be reasoned about (and, in CI, unit-checked) on its own.
10
+ //
11
+ // Every algorithm here was verified against the independent Python reference on the two
12
+ // real bundled images before transcription (crc16 on random vectors; crc32c against both
13
+ // images' stored TOC CRCs; the guard against the captured golden vectors below).
14
+ // Portions of the protocol are derived from that reference implementation.
15
+ //
16
+
17
+ import Foundation
18
+
19
+ enum G2FlashProtocolError: Error, CustomStringConvertible {
20
+ case badImage(String)
21
+ var description: String {
22
+ switch self { case let .badImage(m): return "bad firmware image: \(m)" }
23
+ }
24
+ }
25
+
26
+ enum G2FlashProtocol {
27
+ /// ---- CRC16 (CCITT, init 0xFFFF, poly 0x1021) — the OTA body trailer, LE bytes. ----
28
+ static func crc16(_ data: [UInt8]) -> [UInt8] {
29
+ BinaryCodec.crc16CCITTLittleEndian(data)
30
+ }
31
+
32
+ /// ---- CRC32C (Castagnoli, MSB-first, init 0, xorout 0) — per-component payload CRC. ----
33
+ private static let crc32cTable: [UInt32] = {
34
+ var t = [UInt32](repeating: 0, count: 256)
35
+ for b in 0 ..< 256 {
36
+ var c = UInt32(b) << 24
37
+ for _ in 0 ..< 8 {
38
+ c = (c & 0x8000_0000) != 0 ? (c << 1) ^ 0x1EDC_6F41 : (c << 1)
39
+ }
40
+ t[b] = c
41
+ }
42
+ return t
43
+ }()
44
+
45
+ static func crc32c(_ buf: [UInt8]) -> UInt32 {
46
+ var crc: UInt32 = 0
47
+ for byte in buf {
48
+ crc = (crc << 8) ^ crc32cTable[Int(((crc >> 24) ^ UInt32(byte)) & 0xFF)]
49
+ }
50
+ return crc
51
+ }
52
+
53
+ /// ---- little-endian u32 read ----
54
+ static func readU32LE(_ b: [UInt8], _ o: Int) -> UInt32 {
55
+ return UInt32(b[o]) | (UInt32(b[o + 1]) << 8) | (UInt32(b[o + 2]) << 16) | (UInt32(b[o + 3]) << 24)
56
+ }
57
+
58
+ /// ---- OTA transport framing (validated byte-for-byte against the Python reference) ----
59
+ /// body = pb + crc16(pb); chunk into 232-byte frames; per frame:
60
+ /// [0xAA, 0x21, seq, len(chunk), tot, serial(1-based), sid, flag] + chunk
61
+ /// sid = 0xC0 ctrl, 0xC1 data. A shared `seq` links a marker+block pair.
62
+ static let CHUNK = 232
63
+
64
+ static func frames(sid: UInt8, pb: [UInt8], flag: UInt8 = 0, seq: UInt8) -> [[UInt8]] {
65
+ let body = pb + crc16(pb)
66
+ let tot = max(1, (body.count + CHUNK - 1) / CHUNK)
67
+ var out: [[UInt8]] = []
68
+ var off = 0
69
+ for i in 0 ..< tot {
70
+ let end = min(off + CHUNK, body.count)
71
+ let ch = Array(body[off ..< end])
72
+ off = end
73
+ var frame: [UInt8] = [0xAA, 0x21, seq, UInt8(ch.count), UInt8(tot), UInt8(i + 1), sid, flag]
74
+ frame.append(contentsOf: ch)
75
+ out.append(frame)
76
+ }
77
+ return out
78
+ }
79
+
80
+ static func ctrlFrames(_ op: UInt8, _ data: [UInt8] = [], seq: UInt8) -> [[UInt8]] {
81
+ return frames(sid: 0xC0, pb: [op] + data, seq: seq)
82
+ }
83
+
84
+ static func dataFrames(_ block: [UInt8], seq: UInt8) -> [[UInt8]] {
85
+ return frames(sid: 0xC1, pb: block, seq: seq)
86
+ }
87
+
88
+ /// Stock DEVICE_SETTINGS/AUTHENTICATION request sent before every OTA BEGIN.
89
+ /// The phone type is iOS (3); the captured Android request uses phone type 4.
90
+ static func authenticationFrames(magic: UInt8) -> [[UInt8]] {
91
+ let pb: [UInt8] = [
92
+ 0x08, 0x04, // commandId = AUTHENTICATION
93
+ 0x10, magic, // magicRandom
94
+ 0x1A, 0x04, 0x08, 0x01, 0x10, 0x03, // AuthMgr { secAuth=true, phoneType=iOS }
95
+ ]
96
+ return frames(sid: 0x80, pb: pb, seq: magic)
97
+ }
98
+
99
+ /// Accept the successful stock authentication response: matching command and
100
+ /// magic, followed by an empty result message (the default result code is zero).
101
+ static func isAuthenticationSuccess(_ frame: [UInt8], magic: UInt8) -> Bool {
102
+ guard let decoded = parseRx(frame), decoded.sid == 0x80 else { return false }
103
+ return decoded.pb == [0x08, 0x04, 0x10, magic, 0x1A, 0x00]
104
+ }
105
+
106
+ /// Unwrap an `aa12` reply envelope → (sequence, sid, pb).
107
+ /// pb = [opcode, status, ...].
108
+ static func parseRx(_ frame: [UInt8]) -> (sequence: UInt8, sid: UInt8, pb: [UInt8])? {
109
+ guard frame.count >= 10, frame[0] == 0xAA, frame[1] == 0x12 else { return nil }
110
+ let sequence = frame[2]
111
+ let ln = Int(frame[3])
112
+ let sid = frame[6]
113
+ let n = max(0, ln - 2)
114
+ let end = min(8 + n, frame.count)
115
+ guard end >= 8 else { return (sequence, sid, []) }
116
+ return (sequence, sid, Array(frame[8 ..< end]))
117
+ }
118
+
119
+ /// OTA replies are correlated by notification characteristic + opcode. The
120
+ /// firmware does not promise to echo the request's AA21 sequence or SID in
121
+ /// its AA12 response (and real block ACKs do not consistently do so).
122
+ static func otaAckStatus(in frames: [[UInt8]], opcode: UInt8) -> UInt8? {
123
+ for frame in frames {
124
+ guard let decoded = parseRx(frame),
125
+ decoded.pb.count >= 2,
126
+ decoded.pb[0] == opcode
127
+ else { continue }
128
+ return decoded.pb[1]
129
+ }
130
+ return nil
131
+ }
132
+
133
+ /// ---- EVENOTA container parsing ----
134
+ struct Segment {
135
+ let eid: UInt32
136
+ let off: UInt32
137
+ let size: UInt32
138
+ let crc: UInt32
139
+ let sub: [UInt8] // 128-byte subheader
140
+ let ps: UInt32 // payload size
141
+ let fn: String // component name
142
+ }
143
+
144
+ struct ValidatedImage {
145
+ let segments: [Segment]
146
+ let guardResult: GuardResult
147
+ }
148
+
149
+ static func parseSegments(_ img: [UInt8]) throws -> [Segment] {
150
+ guard img.count >= 0x40 else { throw G2FlashProtocolError.badImage("file too small") }
151
+ let n = readU32LE(img, 8)
152
+ guard n > 0 && n <= 64 else { throw G2FlashProtocolError.badImage("implausible component count \(n)") }
153
+ var segs: [Segment] = []
154
+ for i in 0 ..< Int(n) {
155
+ let base = 0x40 + i * 16
156
+ guard base + 16 <= img.count else { throw G2FlashProtocolError.badImage("TOC entry \(i) past EOF") }
157
+ let eid = readU32LE(img, base)
158
+ let off = readU32LE(img, base + 4)
159
+ let size = readU32LE(img, base + 8)
160
+ let crc = readU32LE(img, base + 12)
161
+ let so = Int(off)
162
+ guard so >= 0, so + 128 <= img.count else {
163
+ throw G2FlashProtocolError.badImage("segment \(i) subheader past EOF")
164
+ }
165
+ let sub = Array(img[so ..< so + 128])
166
+ let ps = readU32LE(sub, 8)
167
+ let nameBytes = sub[48 ..< 128].prefix(while: { $0 != 0 })
168
+ let fn = String(bytes: nameBytes, encoding: .isoLatin1) ?? ""
169
+ segs.append(Segment(eid: eid, off: off, size: size, crc: crc, sub: sub, ps: ps, fn: fn))
170
+ }
171
+ return segs
172
+ }
173
+
174
+ /// Full preflight for every image, regardless of where it came from. This
175
+ /// validates structure, each component's payload CRC32C against both stored
176
+ /// copies, and the main-app MRAM bounds before any BLE write is allowed.
177
+ static func validateFirmware(_ img: [UInt8]) throws -> ValidatedImage {
178
+ let segments = try parseSegments(img)
179
+ let names = segments.map(\.fn)
180
+ guard (5 ... 6).contains(segments.count) else {
181
+ throw G2FlashProtocolError.badImage(
182
+ "expected 5-6 segments, found \(segments.count): \(names)"
183
+ )
184
+ }
185
+ guard names.contains(REQUIRED_SEGMENT) else {
186
+ throw G2FlashProtocolError.badImage(
187
+ "required segment \(REQUIRED_SEGMENT) not found; segments are: \(names)"
188
+ )
189
+ }
190
+
191
+ for (index, segment) in segments.enumerated() {
192
+ let payloadOffset = Int(segment.off) + 128
193
+ let payloadSize = Int(segment.ps)
194
+ guard payloadSize > 0, payloadOffset >= 128,
195
+ payloadOffset <= img.count,
196
+ payloadSize <= img.count - payloadOffset
197
+ else {
198
+ throw G2FlashProtocolError.badImage(
199
+ "segment \(index) (\(segment.fn)) payload extends past EOF"
200
+ )
201
+ }
202
+ guard segment.ps <= UInt32.max - 128,
203
+ segment.size == segment.ps + 128
204
+ else {
205
+ throw G2FlashProtocolError.badImage(
206
+ "segment \(index) (\(segment.fn)) TOC size \(segment.size) != payload+header \(segment.ps + 128)"
207
+ )
208
+ }
209
+
210
+ let payload = Array(img[payloadOffset ..< payloadOffset + payloadSize])
211
+ let calculated = crc32c(payload)
212
+ let subheaderCRC = readU32LE(segment.sub, 12)
213
+ guard calculated == segment.crc, calculated == subheaderCRC else {
214
+ throw G2FlashProtocolError.badImage(
215
+ String(
216
+ format: "segment %d (%@) CRC32C mismatch: payload=%08x TOC=%08x sub=%08x",
217
+ index, segment.fn, calculated, segment.crc, subheaderCRC
218
+ )
219
+ )
220
+ }
221
+ }
222
+
223
+ let guardResult = checkMainAppFitsMram(img, segments)
224
+ guard guardResult.pass else {
225
+ throw G2FlashProtocolError.badImage("brick guard blocked image: \(guardResult.reason)")
226
+ }
227
+ return ValidatedImage(segments: segments, guardResult: guardResult)
228
+ }
229
+
230
+ // ---- MRAM brick-guard (the ONLY thing preventing a hard, SWD-only brick) ----
231
+ static let APP_LOAD_ADDR: UInt32 = 0x0043_8000
232
+ static let APP_MAX_END: UInt32 = 0x007F_0000
233
+ static let OTA_FLAG_ADDR: UInt32 = 0x007F_E000
234
+ static let MRAM_END: UInt32 = 0x0080_0000
235
+ static let APP_PREAMBLE = 0x20
236
+ static let REQUIRED_SEGMENT = "ota/s200_firmware_ota.bin"
237
+
238
+ struct GuardResult {
239
+ let ps: UInt32
240
+ let loadAddr: UInt32
241
+ let preLen: UInt32
242
+ let progEnd: UInt32
243
+ let pass: Bool
244
+ let reason: String
245
+ }
246
+
247
+ /// Re-derives the guard directly from the image bytes using the independently
248
+ /// verified MRAM-fit algorithm. Call before EVERY flash (never cache).
249
+ static func checkMainAppFitsMram(_ img: [UInt8], _ segs: [Segment]) -> GuardResult {
250
+ guard let s = segs.first(where: { $0.fn == REQUIRED_SEGMENT }) else {
251
+ return GuardResult(ps: 0, loadAddr: 0, preLen: 0, progEnd: 0, pass: false,
252
+ reason: "main-app segment \(REQUIRED_SEGMENT) not found")
253
+ }
254
+ let ps = s.ps
255
+ let po = Int(s.off) + 128
256
+ guard po + APP_PREAMBLE <= img.count else {
257
+ return GuardResult(ps: ps, loadAddr: 0, preLen: 0, progEnd: 0, pass: false,
258
+ reason: "main-app payload smaller than its 32-byte preamble")
259
+ }
260
+ let pre = Array(img[po ..< po + APP_PREAMBLE])
261
+ let loadAddr = readU32LE(pre, 0x14)
262
+ let preLen = readU32LE(pre, 0) & 0x00FF_FFFF
263
+ if loadAddr != APP_LOAD_ADDR {
264
+ return GuardResult(ps: ps, loadAddr: loadAddr, preLen: preLen, progEnd: 0, pass: false,
265
+ reason: String(format: "load addr 0x%08x != 0x00438000", loadAddr))
266
+ }
267
+ if preLen != ps {
268
+ return GuardResult(ps: ps, loadAddr: loadAddr, preLen: preLen, progEnd: 0, pass: false,
269
+ reason: "preamble length \(preLen) != staged payload size \(ps)")
270
+ }
271
+ let progEnd = APP_LOAD_ADDR &+ ps &- UInt32(APP_PREAMBLE)
272
+ if progEnd > APP_MAX_END {
273
+ return GuardResult(ps: ps, loadAddr: loadAddr, preLen: preLen, progEnd: progEnd, pass: false,
274
+ reason: String(format: "too large: prog_end 0x%08x past ceiling 0x%08x", progEnd, APP_MAX_END))
275
+ }
276
+ return GuardResult(ps: ps, loadAddr: loadAddr, preLen: preLen, progEnd: progEnd, pass: true, reason: "ok")
277
+ }
278
+
279
+ /// ---- Golden-vector self-test (council fix #2) ----
280
+ /// Captured from the Python reference on the exact bundled stock, SHA-verified image.
281
+ /// The Swift guard MUST reproduce these; a mis-transcribed constant that silently
282
+ /// disabled the guard would fail this test (and we refuse to flash if it does).
283
+ struct GoldenVector {
284
+ let sha256: String
285
+ let ps: UInt32
286
+ let progEnd: UInt32
287
+ let pass: Bool
288
+ }
289
+
290
+ static let goldenStock = GoldenVector(
291
+ sha256: "f4dfb0b49ad3de3c2daf17f8a27a157c3dc98411d6a0d3ab2cfd0918f41b9afa",
292
+ ps: 3_523_396, progEnd: 0x0079_4324, pass: true
293
+ )
294
+
295
+ /// Run the parse+guard on `img` and assert it reproduces the golden vector. Returns
296
+ /// nil on success, or a failure description. Any non-nil result MUST block flashing.
297
+ static func selfTestGuard(_ img: [UInt8], expect gv: GoldenVector) -> String? {
298
+ let segs: [Segment]
299
+ do { segs = try parseSegments(img) } catch { return "parse failed: \(error)" }
300
+ let r = checkMainAppFitsMram(img, segs)
301
+ if r.pass != gv.pass {
302
+ return "guard pass=\(r.pass) != golden \(gv.pass)"
303
+ }
304
+ if r.ps != gv.ps {
305
+ return "ps \(r.ps) != golden \(gv.ps)"
306
+ }
307
+ if r.progEnd != gv.progEnd {
308
+ return String(format: "prog_end 0x%08x != golden 0x%08x", r.progEnd, gv.progEnd)
309
+ }
310
+ return nil
311
+ }
312
+ }