@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.
- package/README.md +563 -0
- package/android/build.gradle +450 -0
- package/android/gradle.properties.example +9 -0
- package/android/lc3Lib/CMakeLists.txt +6 -0
- package/android/lc3Lib/build.gradle +212 -0
- package/android/lc3Lib/proguard-rules.pro +33 -0
- package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
- package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
- package/android/lc3Lib/src/main/java/com/glassly/lc3Lib/Lc3Cpp.java +38 -0
- package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
- package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
- package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
- package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
- package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
- package/android/settings.gradle +4 -0
- package/android/silero/LICENSE.md +17 -0
- package/android/silero/build.gradle +50 -0
- package/android/silero/proguard-rules.pro +24 -0
- package/android/silero/src/androidTest/assets/hello.wav +0 -0
- package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
- package/android/silero/src/main/assets/LICENSE +21 -0
- package/android/silero/src/main/assets/silero_vad.onnx +0 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
- package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
- package/android/src/main/AndroidManifest.xml +42 -0
- package/android/src/main/assets/G2Firmware/README.md +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2325 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1088 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2528 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +360 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +8 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +937 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3878 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6428 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1241 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1280 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10817 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2011 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
- package/android/src/test/resources/avif_with_exif.avif +0 -0
- package/app.plugin.js +3 -0
- package/build/BluetoothSdk.types.d.ts +1354 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -0
- package/build/BluetoothSdk.types.js +41 -0
- package/build/BluetoothSdk.types.js.map +1 -0
- package/build/_internal.d.ts +17 -0
- package/build/_internal.d.ts.map +1 -0
- package/build/_internal.js +16 -0
- package/build/_internal.js.map +1 -0
- package/build/_private/BluetoothSdkModule.d.ts +172 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
- package/build/_private/BluetoothSdkModule.js +300 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -0
- package/build/_private/LocalNetworkModule.d.ts +38 -0
- package/build/_private/LocalNetworkModule.d.ts.map +1 -0
- package/build/_private/LocalNetworkModule.js +3 -0
- package/build/_private/LocalNetworkModule.js.map +1 -0
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +32 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts +6 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -0
- package/build/_private/photoRequestPayload.js +86 -0
- package/build/_private/photoRequestPayload.js.map +1 -0
- package/build/_private/sdkOtaManifest.d.ts +11 -0
- package/build/_private/sdkOtaManifest.d.ts.map +1 -0
- package/build/_private/sdkOtaManifest.js +15 -0
- package/build/_private/sdkOtaManifest.js.map +1 -0
- package/build/changelogs.d.ts +7 -0
- package/build/changelogs.d.ts.map +1 -0
- package/build/changelogs.js +46 -0
- package/build/changelogs.js.map +1 -0
- package/build/debug.d.ts +3 -0
- package/build/debug.d.ts.map +1 -0
- package/build/debug.js +8 -0
- package/build/debug.js.map +1 -0
- package/build/generated/changelogCatalog.d.ts +9 -0
- package/build/generated/changelogCatalog.d.ts.map +1 -0
- package/build/generated/changelogCatalog.js +12 -0
- package/build/generated/changelogCatalog.js.map +1 -0
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +171 -0
- package/build/index.js.map +1 -0
- package/build/ota-server/OtaServer.types.d.ts +14 -0
- package/build/ota-server/OtaServer.types.d.ts.map +1 -0
- package/build/ota-server/OtaServer.types.js +2 -0
- package/build/ota-server/OtaServer.types.js.map +1 -0
- package/build/ota-server/OtaServerModule.d.ts +21 -0
- package/build/ota-server/OtaServerModule.d.ts.map +1 -0
- package/build/ota-server/OtaServerModule.js +3 -0
- package/build/ota-server/OtaServerModule.js.map +1 -0
- package/build/ota-server/OtaServerModule.web.d.ts +10 -0
- package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
- package/build/ota-server/OtaServerModule.web.js +12 -0
- package/build/ota-server/OtaServerModule.web.js.map +1 -0
- package/build/ota-server/index.d.ts +3 -0
- package/build/ota-server/index.d.ts.map +1 -0
- package/build/ota-server/index.js +3 -0
- package/build/ota-server/index.js.map +1 -0
- package/build/ota-transport/index.d.ts +67 -0
- package/build/ota-transport/index.d.ts.map +1 -0
- package/build/ota-transport/index.js +58 -0
- package/build/ota-transport/index.js.map +1 -0
- package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
- package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiver.types.js +2 -0
- package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
- package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.js +3 -0
- package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
- package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
- package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
- package/build/photo-receiver/index.d.ts +3 -0
- package/build/photo-receiver/index.d.ts.map +1 -0
- package/build/photo-receiver/index.js +3 -0
- package/build/photo-receiver/index.js.map +1 -0
- package/build/react/index.d.ts +5 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +4 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useBluetooth.d.ts +100 -0
- package/build/react/useBluetooth.d.ts.map +1 -0
- package/build/react/useBluetooth.js +167 -0
- package/build/react/useBluetooth.js.map +1 -0
- package/build/react/useBluetoothEvent.d.ts +6 -0
- package/build/react/useBluetoothEvent.d.ts.map +1 -0
- package/build/react/useBluetoothEvent.js +21 -0
- package/build/react/useBluetoothEvent.js.map +1 -0
- package/build/react/useBluetoothScan.d.ts +22 -0
- package/build/react/useBluetoothScan.d.ts.map +1 -0
- package/build/react/useBluetoothScan.js +135 -0
- package/build/react/useBluetoothScan.js.map +1 -0
- package/build/react/useBluetoothStatus.d.ts +16 -0
- package/build/react/useBluetoothStatus.d.ts.map +1 -0
- package/build/react/useBluetoothStatus.js +137 -0
- package/build/react/useBluetoothStatus.js.map +1 -0
- package/build/react/useGlassesConnection.d.ts +29 -0
- package/build/react/useGlassesConnection.d.ts.map +1 -0
- package/build/react/useGlassesConnection.js +153 -0
- package/build/react/useGlassesConnection.js.map +1 -0
- package/build/types/index.d.ts +3 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/index.js +2 -0
- package/build/types/index.js.map +1 -0
- package/expo-module.config.json +14 -0
- package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
- package/ios/BluetoothSdkModule.swift +1034 -0
- package/ios/GlasslyBluetoothSDK.podspec +106 -0
- package/ios/LocalIPv4.swift +150 -0
- package/ios/LocalOtaServer.swift +309 -0
- package/ios/LocalPhotoUploadServer.swift +754 -0
- package/ios/OtaServerModule.swift +147 -0
- package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
- package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
- package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
- package/ios/Packages/CoreObjC/attdet.c +92 -0
- package/ios/Packages/CoreObjC/attdet.h +44 -0
- package/ios/Packages/CoreObjC/bits.c +375 -0
- package/ios/Packages/CoreObjC/bits.h +315 -0
- package/ios/Packages/CoreObjC/bwdet.c +129 -0
- package/ios/Packages/CoreObjC/bwdet.h +69 -0
- package/ios/Packages/CoreObjC/common.h +151 -0
- package/ios/Packages/CoreObjC/energy.c +70 -0
- package/ios/Packages/CoreObjC/energy.h +43 -0
- package/ios/Packages/CoreObjC/fastmath.h +158 -0
- package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
- package/ios/Packages/CoreObjC/lc3.c +704 -0
- package/ios/Packages/CoreObjC/lc3.h +313 -0
- package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
- package/ios/Packages/CoreObjC/lc3_private.h +163 -0
- package/ios/Packages/CoreObjC/ltpf.c +905 -0
- package/ios/Packages/CoreObjC/ltpf.h +111 -0
- package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
- package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
- package/ios/Packages/CoreObjC/makefile.mk +35 -0
- package/ios/Packages/CoreObjC/mdct.c +469 -0
- package/ios/Packages/CoreObjC/mdct.h +57 -0
- package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
- package/ios/Packages/CoreObjC/meson.build +61 -0
- package/ios/Packages/CoreObjC/plc.c +61 -0
- package/ios/Packages/CoreObjC/plc.h +57 -0
- package/ios/Packages/CoreObjC/rnnoise.h +114 -0
- package/ios/Packages/CoreObjC/sns.c +880 -0
- package/ios/Packages/CoreObjC/sns.h +103 -0
- package/ios/Packages/CoreObjC/spec.c +907 -0
- package/ios/Packages/CoreObjC/spec.h +119 -0
- package/ios/Packages/CoreObjC/tables.c +3457 -0
- package/ios/Packages/CoreObjC/tables.h +94 -0
- package/ios/Packages/CoreObjC/tns.c +457 -0
- package/ios/Packages/CoreObjC/tns.h +99 -0
- package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
- package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
- package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
- package/ios/Packages/libbz2/module.modulemap +5 -0
- package/ios/Packages/libbz2/shim.h +1 -0
- package/ios/PhotoReceiverModule.swift +129 -0
- package/ios/Source/BluetoothSDK.swift +2303 -0
- package/ios/Source/BluetoothSDKDebug.swift +12 -0
- package/ios/Source/BluetoothSdkDefaults.swift +29 -0
- package/ios/Source/Bridge.swift +708 -0
- package/ios/Source/Bridging-Header.h +16 -0
- package/ios/Source/DeviceManager.swift +2109 -0
- package/ios/Source/DeviceStore.swift +336 -0
- package/ios/Source/GeneratedChangelogCatalog.swift +7 -0
- package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
- package/ios/Source/ObservableStore.swift +118 -0
- package/ios/Source/OtaManifest.swift +211 -0
- package/ios/Source/PrivacyInfo.xcprivacy +23 -0
- package/ios/Source/ReleaseChangelog.swift +65 -0
- package/ios/Source/Resources/G2Firmware/README.md +10 -0
- package/ios/Source/audio/AudioModels.swift +178 -0
- package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
- package/ios/Source/camera/CameraModels.swift +866 -0
- package/ios/Source/connection/ScanSession.swift +27 -0
- package/ios/Source/controllers/ControllerManager.swift +184 -0
- package/ios/Source/controllers/R1.swift +829 -0
- package/ios/Source/errors/BluetoothSdkError.swift +19 -0
- package/ios/Source/events/BluetoothEvents.swift +342 -0
- package/ios/Source/internal/BleTraceLogger.swift +192 -0
- package/ios/Source/internal/BluetoothAvailability.swift +78 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
- package/ios/Source/internal/ValueParsing.swift +99 -0
- package/ios/Source/requests/DisplayRequests.swift +82 -0
- package/ios/Source/services/PhoneMic.swift +662 -0
- package/ios/Source/sgcs/Ar99.swift +2451 -0
- package/ios/Source/sgcs/Frame.swift +649 -0
- package/ios/Source/sgcs/G1.swift +2554 -0
- package/ios/Source/sgcs/G2.swift +6887 -0
- package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
- package/ios/Source/sgcs/G2CfwRenderer.swift +1279 -0
- package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
- package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
- package/ios/Source/sgcs/G2Flasher.swift +773 -0
- package/ios/Source/sgcs/Mach1.swift +541 -0
- package/ios/Source/sgcs/MentraLive.swift +7011 -0
- package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
- package/ios/Source/sgcs/MentraNex.swift +3221 -0
- package/ios/Source/sgcs/Nimo.swift +1886 -0
- package/ios/Source/sgcs/SGCManager.swift +608 -0
- package/ios/Source/sgcs/Simulated.swift +274 -0
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
- package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
- package/ios/Source/status/DeviceStatus.swift +981 -0
- package/ios/Source/status/RuntimeState.swift +359 -0
- package/ios/Source/status/WifiHotspotStatus.swift +345 -0
- package/ios/Source/streaming/StreamModels.swift +693 -0
- package/ios/Source/stt/STTTools.swift +191 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
- package/ios/Source/tts/TTSTools.swift +171 -0
- package/ios/Source/types/DeviceModels.swift +177 -0
- package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
- package/ios/Source/utils/BinaryCodec.swift +61 -0
- package/ios/Source/utils/BleJsonCompact.swift +405 -0
- package/ios/Source/utils/BleWireProtocol.swift +92 -0
- package/ios/Source/utils/Constants.swift +78 -0
- package/ios/Source/utils/Enums.swift +95 -0
- package/ios/Source/utils/G1Text.swift +2096 -0
- package/ios/Source/utils/JSCExperiment.swift +241 -0
- package/ios/Source/utils/MemoryMonitor.swift +44 -0
- package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
- package/ios/Source/utils/MessageChunker.swift +294 -0
- package/ios/Source/utils/Models.swift +135 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
- package/ios/Source/utils/TarBz2Extractor.swift +257 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
- package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
- package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
- package/ios/Tests/ReleaseChangelogTests.swift +13 -0
- package/package.json +129 -0
- package/plugin/build/index.d.ts +10 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.d.ts +3 -0
- package/plugin/build/withAndroid.js +190 -0
- package/plugin/build/withIos.d.ts +5 -0
- package/plugin/build/withIos.js +108 -0
- package/scripts/inject-ios-sdk-version.mjs +65 -0
- package/scripts/public-ota-api.test.mjs +37 -0
- package/scripts/verify-release-package.mjs +45 -0
- package/scripts/write-release-metadata.mjs +152 -0
- package/scripts/write-release-metadata.test.mjs +37 -0
- package/src/BluetoothSdk.types.ts +1623 -0
- package/src/_internal.ts +16 -0
- package/src/_private/BluetoothSdkModule.ts +684 -0
- package/src/_private/LocalNetworkModule.ts +50 -0
- package/src/_private/cameraRequestPayload.ts +34 -0
- package/src/_private/photoRequestPayload.ts +90 -0
- package/src/_private/sdkOtaManifest.ts +18 -0
- package/src/changelogs.ts +46 -0
- package/src/debug.ts +9 -0
- package/src/generated/changelogCatalog.ts +13 -0
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +321 -0
- package/src/ota-server/OtaServer.types.ts +10 -0
- package/src/ota-server/OtaServerModule.ts +23 -0
- package/src/ota-server/OtaServerModule.web.ts +17 -0
- package/src/ota-server/index.ts +2 -0
- package/src/ota-transport/index.ts +116 -0
- package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
- package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
- package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
- package/src/photo-receiver/index.ts +2 -0
- package/src/react/index.ts +21 -0
- package/src/react/useBluetooth.ts +309 -0
- package/src/react/useBluetoothEvent.ts +35 -0
- package/src/react/useBluetoothScan.ts +173 -0
- package/src/react/useBluetoothStatus.ts +180 -0
- package/src/react/useGlassesConnection.ts +202 -0
- 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
|
+
}
|