@glassly/bluetooth-sdk 0.1.0-dev.100
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/assets/G2Firmware/g2-glassly-cfw-2.2.9.22.evenota.bin +0 -0
- package/android/src/main/assets/G2Firmware/g2-stock-2.2.9.22.evenota.bin +0 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2321 -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 +1087 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2472 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +359 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +9 -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 +1057 -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 +3837 -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 +6457 -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 +1253 -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 +1267 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10742 -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 +2008 -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 +227 -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 +1390 -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 +171 -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 +12 -0
- package/build/generated/changelogCatalog.d.ts.map +1 -0
- package/build/generated/changelogCatalog.js +16 -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 +170 -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 +1028 -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 +2299 -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 +2054 -0
- package/ios/Source/DeviceStore.swift +350 -0
- package/ios/Source/GeneratedChangelogCatalog.swift +8 -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/Resources/G2Firmware/g2-glassly-cfw-2.2.9.22.evenota.bin +0 -0
- package/ios/Source/Resources/G2Firmware/g2-stock-2.2.9.22.evenota.bin +0 -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 +925 -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/G1.swift +2554 -0
- package/ios/Source/sgcs/G2.swift +7142 -0
- package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
- package/ios/Source/sgcs/G2CfwRenderer.swift +1295 -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 +3220 -0
- package/ios/Source/sgcs/Nimo.swift +1886 -0
- package/ios/Source/sgcs/SGCManager.swift +621 -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 +167 -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/G2NotifyFile.swift +116 -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/BluetoothSDKTests/G2NotifyFileTests.swift +91 -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 +46 -0
- package/scripts/write-release-metadata.mjs +154 -0
- package/scripts/write-release-metadata.test.mjs +37 -0
- package/src/BluetoothSdk.types.ts +1659 -0
- package/src/_internal.ts +16 -0
- package/src/_private/BluetoothSdkModule.ts +683 -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 +17 -0
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +320 -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,116 @@
|
|
|
1
|
+
//
|
|
2
|
+
// G2NotifyFile.swift
|
|
3
|
+
// BluetoothSDK
|
|
4
|
+
//
|
|
5
|
+
// Pure helpers for the G2 notification whitelist file push
|
|
6
|
+
// (`user/notify_whitelist.json` over the 0xC4/0xC5 file-transfer services).
|
|
7
|
+
// Kept free of CoreBluetooth so the wire format is unit-testable.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import Foundation
|
|
11
|
+
|
|
12
|
+
enum G2NotifyFile {
|
|
13
|
+
static let whitelistFilename = "user/notify_whitelist.json"
|
|
14
|
+
|
|
15
|
+
/// Firmware caps: 100-app list, 80-byte records (64-byte identifier
|
|
16
|
+
/// including NUL, 16-byte display name including NUL).
|
|
17
|
+
static let maxApps = 100
|
|
18
|
+
static let maxIdentifierBytes = 63
|
|
19
|
+
static let maxDisplayNameBytes = 15
|
|
20
|
+
|
|
21
|
+
/// CRC-32C (Castagnoli), init 0, non-reflected (MSB-first) — the variant the
|
|
22
|
+
/// G2 file-transfer FILE_CHECK header uses, NOT the common reflected crc32c.
|
|
23
|
+
private static let crc32cTable: [UInt32] = {
|
|
24
|
+
var table = [UInt32](repeating: 0, count: 256)
|
|
25
|
+
for i in 0 ..< 256 {
|
|
26
|
+
var crc = UInt32(i) << 24
|
|
27
|
+
for _ in 0 ..< 8 {
|
|
28
|
+
crc = (crc & 0x8000_0000) != 0 ? (crc << 1) ^ 0x1EDC_6F41 : crc << 1
|
|
29
|
+
}
|
|
30
|
+
table[i] = crc
|
|
31
|
+
}
|
|
32
|
+
return table
|
|
33
|
+
}()
|
|
34
|
+
|
|
35
|
+
static func crc32c(_ data: Data) -> UInt32 {
|
|
36
|
+
var crc: UInt32 = 0
|
|
37
|
+
for byte in data {
|
|
38
|
+
let idx = Int((crc >> 24) ^ UInt32(byte)) & 0xFF
|
|
39
|
+
crc = (crc << 8) ^ crc32cTable[idx]
|
|
40
|
+
}
|
|
41
|
+
return crc
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// 93-byte FILE_CHECK payload: mode, size, split CRC-32C, 80-byte
|
|
45
|
+
/// NUL-padded filename. The glasses reconstruct the checksum as
|
|
46
|
+
/// `(extra << 24) | (checksum >> 8)`.
|
|
47
|
+
static func fileCheckHeader(filename: String, content: Data) -> Data {
|
|
48
|
+
let crc = crc32c(content)
|
|
49
|
+
var header = Data()
|
|
50
|
+
// Mode 0x100: the even-g2-protocol docs' prose says 0x00010000, but the
|
|
51
|
+
// working example (`notification.py`) packs `<I` 0x100 — trust the code.
|
|
52
|
+
header.appendLittleEndianUInt32(0x100) // mode
|
|
53
|
+
header.appendLittleEndianUInt32(UInt32(content.count) &* 256) // size
|
|
54
|
+
header.appendLittleEndianUInt32(crc << 8) // checksum (low 24 bits, shifted)
|
|
55
|
+
header.append(UInt8(crc >> 24)) // extra (high CRC byte)
|
|
56
|
+
var name = Array(filename.utf8)
|
|
57
|
+
if name.count > 80 {
|
|
58
|
+
name = Array(name.prefix(80))
|
|
59
|
+
}
|
|
60
|
+
header.append(contentsOf: name)
|
|
61
|
+
header.append(contentsOf: [UInt8](repeating: 0, count: 80 - name.count))
|
|
62
|
+
return header
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Whitelist JSON in the stock app's shape (AppNotificationInfo /
|
|
66
|
+
/// AppWhiteList / AppSimpleInfo from the decompiled Even app):
|
|
67
|
+
/// `{call_enable, msg_enable, calendar_enable, ios_mail_enable,
|
|
68
|
+
/// app: {enable, list: [{id, name}]}}`
|
|
69
|
+
/// Entries with identifiers over 63 UTF-8 bytes are invalid to the firmware
|
|
70
|
+
/// and dropped; display names truncate to 15 UTF-8 bytes.
|
|
71
|
+
static func whitelistJson(filterEnabled: Bool, apps: [(id: String, name: String)]) -> Data {
|
|
72
|
+
var list: [[String: String]] = []
|
|
73
|
+
for app in apps {
|
|
74
|
+
guard !app.id.isEmpty, app.id.utf8.count <= maxIdentifierBytes else { continue }
|
|
75
|
+
let name = app.name.isEmpty ? app.id : app.name
|
|
76
|
+
list.append(["id": app.id, "name": truncateUtf8(name, maxBytes: maxDisplayNameBytes)])
|
|
77
|
+
if list.count >= maxApps {
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let payload: [String: Any] = [
|
|
82
|
+
"call_enable": true,
|
|
83
|
+
"msg_enable": true,
|
|
84
|
+
"calendar_enable": true,
|
|
85
|
+
"ios_mail_enable": true,
|
|
86
|
+
"app": [
|
|
87
|
+
"enable": filterEnabled,
|
|
88
|
+
"list": list,
|
|
89
|
+
] as [String: Any],
|
|
90
|
+
]
|
|
91
|
+
// sortedKeys keeps the output deterministic (glasses cache by checksum).
|
|
92
|
+
return (try? JSONSerialization.data(withJSONObject: payload, options: [.sortedKeys])) ?? Data()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// Truncate to a UTF-8 byte budget without splitting a code point.
|
|
96
|
+
static func truncateUtf8(_ value: String, maxBytes: Int) -> String {
|
|
97
|
+
if value.utf8.count <= maxBytes {
|
|
98
|
+
return value
|
|
99
|
+
}
|
|
100
|
+
var result = ""
|
|
101
|
+
for char in value {
|
|
102
|
+
if (result + String(char)).utf8.count > maxBytes {
|
|
103
|
+
break
|
|
104
|
+
}
|
|
105
|
+
result.append(char)
|
|
106
|
+
}
|
|
107
|
+
return result
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private extension Data {
|
|
112
|
+
mutating func appendLittleEndianUInt32(_ value: UInt32) {
|
|
113
|
+
var v = value.littleEndian
|
|
114
|
+
Swift.withUnsafeBytes(of: &v) { append(contentsOf: $0) }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JSCExperiment.swift
|
|
3
|
+
// Glassly
|
|
4
|
+
//
|
|
5
|
+
// Test/benchmark infrastructure. Self-contained file, never invoked in
|
|
6
|
+
// normal user flow. Triggered only when (a) GLASSLY_RUN_JSC_BENCH env
|
|
7
|
+
// var is set at app launch, or (b) the user taps a JSC button on the
|
|
8
|
+
// (Super-Mode-gated) stress-test screen.
|
|
9
|
+
//
|
|
10
|
+
// Spike: measure the actual memory cost of N concurrent JSContexts on iOS.
|
|
11
|
+
// We want to know whether "Pebble-style native JSC per miniapp" is viable
|
|
12
|
+
// for our N-concurrent-miniapp use case (Pebble runs only 1 at a time, so
|
|
13
|
+
// they have no data on this).
|
|
14
|
+
//
|
|
15
|
+
// Each spawned context:
|
|
16
|
+
// - Has its own JSVirtualMachine (full isolation, separate heap)
|
|
17
|
+
// - Has __dispatch as a single bridge function (no per-method bindings,
|
|
18
|
+
// to avoid the production crash Pebble documented in CrashReproducer.kt)
|
|
19
|
+
// - Runs a representative idle workload: setInterval ping every 5 s
|
|
20
|
+
// - Names itself "GlasslyJS: <id>" for Safari Web Inspector
|
|
21
|
+
// - Is NOT inspectable in release builds
|
|
22
|
+
//
|
|
23
|
+
// Measure resident memory before and after spawning N contexts.
|
|
24
|
+
|
|
25
|
+
import Foundation
|
|
26
|
+
import JavaScriptCore
|
|
27
|
+
import os.log
|
|
28
|
+
|
|
29
|
+
private let jscLog = OSLog(subsystem: "com.tetramo.glassly", category: "JSCSpike")
|
|
30
|
+
|
|
31
|
+
/// Log to BOTH jlog (forwarded into JS) AND os_log AND a flat file in
|
|
32
|
+
/// the app's Documents directory. Release builds don't pipe RN's
|
|
33
|
+
/// console.log or Swift print() to syslog, and os_log filtering through
|
|
34
|
+
/// idevicesyslog is unreliable. The Documents file is the agentic-test
|
|
35
|
+
/// path: `xcrun devicectl device copy from --domain-type appDataContainer
|
|
36
|
+
/// --domain-identifier com.tetramo.glassly --source Documents/jsc-spike.log`.
|
|
37
|
+
private let logFileURL: URL = {
|
|
38
|
+
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
39
|
+
return docs.appendingPathComponent("jsc-spike.log")
|
|
40
|
+
}()
|
|
41
|
+
|
|
42
|
+
private func jlog(_ message: String) {
|
|
43
|
+
Bridge.log(message)
|
|
44
|
+
os_log("%{public}@", log: jscLog, type: .info, message)
|
|
45
|
+
let stamped = "[\(ISO8601DateFormatter().string(from: Date()))] \(message)\n"
|
|
46
|
+
if let data = stamped.data(using: .utf8) {
|
|
47
|
+
if FileManager.default.fileExists(atPath: logFileURL.path) {
|
|
48
|
+
if let handle = try? FileHandle(forWritingTo: logFileURL) {
|
|
49
|
+
defer { try? handle.close() }
|
|
50
|
+
try? handle.seekToEnd()
|
|
51
|
+
try? handle.write(contentsOf: data)
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
try? data.write(to: logFileURL, options: .atomic)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc class JSCExperiment: NSObject {
|
|
60
|
+
/// Called from BluetoothSdkModule init. If GLASSLY_RUN_JSC_BENCH env var
|
|
61
|
+
/// is set, kick off the benchmark after a 5s settle window so the app
|
|
62
|
+
/// is fully booted (RN bridge up, Metro pull done if dev, etc).
|
|
63
|
+
@objc static func maybeAutoBenchmark() {
|
|
64
|
+
guard ProcessInfo.processInfo.environment["GLASSLY_RUN_JSC_BENCH"] != nil else { return }
|
|
65
|
+
os_log("🧪 GLASSLY_RUN_JSC_BENCH set — auto-running benchmark in 5s",
|
|
66
|
+
log: jscLog, type: .info)
|
|
67
|
+
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 5.0) {
|
|
68
|
+
runBenchmark()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// id → (context, virtualMachine, timer for the workload)
|
|
73
|
+
private static var contexts: [String: (JSContext, JSVirtualMachine, Timer)] = [:]
|
|
74
|
+
private static let queue = DispatchQueue(label: "com.glassly.jsc-experiment")
|
|
75
|
+
|
|
76
|
+
/// Spawn N JSContexts at once. Each gets its own VM + a representative
|
|
77
|
+
/// idle workload. Returns the count successfully spawned.
|
|
78
|
+
@objc static func spawn(count: Int) -> Int {
|
|
79
|
+
let beforeMB = MemoryMonitor.currentMemoryMB()
|
|
80
|
+
jlog("🧪 JSCExperiment.spawn(\(count)) starting; baseline \(String(format: "%.1f", beforeMB)) MB")
|
|
81
|
+
var spawned = 0
|
|
82
|
+
for i in 0 ..< count {
|
|
83
|
+
let id = "spike-\(UUID().uuidString.prefix(8))-\(i)"
|
|
84
|
+
if spawnOne(id: id) {
|
|
85
|
+
spawned += 1
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let afterMB = MemoryMonitor.currentMemoryMB()
|
|
89
|
+
let perMB = spawned > 0 ? (afterMB - beforeMB) / Double(spawned) : 0
|
|
90
|
+
jlog("🧪 JSCExperiment: spawned \(spawned)/\(count) contexts; total alive \(contexts.count); mem \(String(format: "%.1f", beforeMB))→\(String(format: "%.1f", afterMB)) MB (\(String(format: "%+.2f", perMB)) MB/ctx)")
|
|
91
|
+
return spawned
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Spawn one named context. Returns true on success.
|
|
95
|
+
@objc static func spawnOne(id: String) -> Bool {
|
|
96
|
+
return queue.sync {
|
|
97
|
+
// Each context gets its own VM = full heap isolation between miniapps.
|
|
98
|
+
// (JSContexts that share a VM share the heap; we want isolation.)
|
|
99
|
+
let vm = JSVirtualMachine()!
|
|
100
|
+
let ctx = JSContext(virtualMachine: vm)!
|
|
101
|
+
|
|
102
|
+
ctx.name = "GlasslyJS: \(id)"
|
|
103
|
+
// Inspectable in dev builds only. iOS 16.4+ guarded.
|
|
104
|
+
#if DEBUG
|
|
105
|
+
if #available(iOS 16.4, *) {
|
|
106
|
+
ctx.isInspectable = true
|
|
107
|
+
}
|
|
108
|
+
#endif
|
|
109
|
+
|
|
110
|
+
// Single-dispatcher bridge. Per Pebble's CrashReproducer doc, never
|
|
111
|
+
// bind individual native callbacks as JSValue properties — JSC's GC
|
|
112
|
+
// races with ARC and crashes. One C-callable function only.
|
|
113
|
+
let dispatch: @convention(block) (String, String, [Any]?) -> Any? = { _, _, _ in
|
|
114
|
+
// Stub: real impl routes to native services.
|
|
115
|
+
// For the spike we just need __dispatch to exist so the
|
|
116
|
+
// workload can call it without throwing.
|
|
117
|
+
NSNull()
|
|
118
|
+
}
|
|
119
|
+
ctx.setObject(dispatch, forKeyedSubscript: "__dispatch" as NSString)
|
|
120
|
+
|
|
121
|
+
// Representative idle workload — what a typical miniapp's
|
|
122
|
+
// background JS does: hold a tiny bit of state, register a
|
|
123
|
+
// listener, periodically poke the bridge.
|
|
124
|
+
let workload = """
|
|
125
|
+
(function () {
|
|
126
|
+
var counter = 0;
|
|
127
|
+
var state = { id: \"\(id)\", startedAt: Date.now(), notes: [] };
|
|
128
|
+
var i = 0;
|
|
129
|
+
function tick() {
|
|
130
|
+
counter++;
|
|
131
|
+
state.notes.push({ at: Date.now(), n: counter });
|
|
132
|
+
if (state.notes.length > 100) state.notes.shift();
|
|
133
|
+
__dispatch('noop', 'tick', [counter]);
|
|
134
|
+
}
|
|
135
|
+
tick();
|
|
136
|
+
// Schedule a periodic tick. We use the host-provided
|
|
137
|
+
// setInterval (a JS shim that calls into native).
|
|
138
|
+
if (typeof setInterval === 'function') {
|
|
139
|
+
setInterval(tick, 5000);
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
142
|
+
"""
|
|
143
|
+
ctx.evaluateScript(workload)
|
|
144
|
+
|
|
145
|
+
// Workload uses setInterval which is a JS shim that we'd build
|
|
146
|
+
// for real — for the spike we just install a Timer-based
|
|
147
|
+
// setInterval that fires the JS callback. The JS code already
|
|
148
|
+
// expects setInterval to exist as a global.
|
|
149
|
+
let setInterval: @convention(block) (JSValue, Double) -> Int = { fn, ms in
|
|
150
|
+
let id = Int.random(in: 1 ... Int.max)
|
|
151
|
+
// Stub: we won't actually fire in the spike, but the JS
|
|
152
|
+
// callback existing is what we want for memory measurement.
|
|
153
|
+
_ = fn // hold a ref to keep the JS callback alive
|
|
154
|
+
_ = ms
|
|
155
|
+
return id
|
|
156
|
+
}
|
|
157
|
+
ctx.setObject(setInterval, forKeyedSubscript: "setInterval" as NSString)
|
|
158
|
+
|
|
159
|
+
// Idle workload Timer on the native side that does NOTHING —
|
|
160
|
+
// it just exists so the context isn't optimized away. The real
|
|
161
|
+
// JS callback (registered above) will be hit by a real
|
|
162
|
+
// setInterval impl in production.
|
|
163
|
+
let timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in
|
|
164
|
+
// no-op — keep ref alive
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
contexts[id] = (ctx, vm, timer)
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Tear down all contexts. Important: cancel timers, drop refs, force GC.
|
|
173
|
+
@objc static func killAll() {
|
|
174
|
+
queue.sync {
|
|
175
|
+
for (_, entry) in contexts {
|
|
176
|
+
entry.2.invalidate()
|
|
177
|
+
}
|
|
178
|
+
let count = contexts.count
|
|
179
|
+
contexts.removeAll()
|
|
180
|
+
jlog("📊 JSCExperiment: killed all \(count) contexts")
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// How many contexts are alive right now.
|
|
185
|
+
@objc static func aliveCount() -> Int {
|
|
186
|
+
return queue.sync { contexts.count }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/// Convenience: spawn N + return memory delta vs baseline. Caller
|
|
190
|
+
/// records baseline via MemoryMonitor.currentMemoryMB() before calling.
|
|
191
|
+
@objc static func spawnAndMeasure(count: Int, baselineMB: Double) -> [String: Any] {
|
|
192
|
+
let beforeMB = MemoryMonitor.currentMemoryMB()
|
|
193
|
+
let spawned = spawn(count: count)
|
|
194
|
+
// Tiny settle delay so allocations complete before we read.
|
|
195
|
+
Thread.sleep(forTimeInterval: 0.5)
|
|
196
|
+
let afterMB = MemoryMonitor.currentMemoryMB()
|
|
197
|
+
let perContextMB = spawned > 0 ? (afterMB - beforeMB) / Double(spawned) : 0
|
|
198
|
+
let result: [String: Any] = [
|
|
199
|
+
"spawned": spawned,
|
|
200
|
+
"totalAlive": aliveCount(),
|
|
201
|
+
"baselineMB": baselineMB,
|
|
202
|
+
"beforeMB": beforeMB,
|
|
203
|
+
"afterMB": afterMB,
|
|
204
|
+
"deltaMB": afterMB - beforeMB,
|
|
205
|
+
"perContextMB": perContextMB,
|
|
206
|
+
]
|
|
207
|
+
jlog("📊 JSCExperiment.spawnAndMeasure: \(result)")
|
|
208
|
+
return result
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// Run a full benchmark sweep: spawn 1, 5, 10, 25, 50 in waves with
|
|
212
|
+
/// 2s settle between, log resident MB before and after each. All
|
|
213
|
+
/// output via jlog so it reaches syslog in release builds.
|
|
214
|
+
@objc static func runBenchmark() {
|
|
215
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
216
|
+
killAll()
|
|
217
|
+
// Clear log file for a fresh run.
|
|
218
|
+
try? FileManager.default.removeItem(at: logFileURL)
|
|
219
|
+
Thread.sleep(forTimeInterval: 1.0)
|
|
220
|
+
let baseline = MemoryMonitor.currentMemoryMB()
|
|
221
|
+
jlog("🧪 JSC-BENCH start; baseline \(String(format: "%.1f", baseline)) MB; logFile=\(logFileURL.path)")
|
|
222
|
+
let waves = [1, 5, 10, 25, 50]
|
|
223
|
+
var prevAlive = 0
|
|
224
|
+
for target in waves {
|
|
225
|
+
let need = target - prevAlive
|
|
226
|
+
if need > 0 {
|
|
227
|
+
let beforeMB = MemoryMonitor.currentMemoryMB()
|
|
228
|
+
_ = spawn(count: need)
|
|
229
|
+
Thread.sleep(forTimeInterval: 1.0)
|
|
230
|
+
let afterMB = MemoryMonitor.currentMemoryMB()
|
|
231
|
+
let perContextOverall = (afterMB - baseline) / Double(target)
|
|
232
|
+
jlog("🧪 JSC-BENCH wave: target=\(target) alive=\(aliveCount()) before=\(String(format: "%.1f", beforeMB))MB after=\(String(format: "%.1f", afterMB))MB delta=\(String(format: "%+.1f", afterMB - beforeMB))MB cumPerCtx=\(String(format: "%.2f", perContextOverall))MB")
|
|
233
|
+
prevAlive = target
|
|
234
|
+
}
|
|
235
|
+
Thread.sleep(forTimeInterval: 2.0)
|
|
236
|
+
}
|
|
237
|
+
let final = MemoryMonitor.currentMemoryMB()
|
|
238
|
+
jlog("🧪 JSC-BENCH done; alive=\(aliveCount()) final=\(String(format: "%.1f", final))MB total-delta=\(String(format: "%+.1f", final - baseline))MB")
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MemoryMonitor.swift
|
|
3
|
+
// Glassly
|
|
4
|
+
//
|
|
5
|
+
// Memory usage monitoring for leak detection
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
class MemoryMonitor {
|
|
11
|
+
private static var timer: Timer?
|
|
12
|
+
private static var startMemoryMB: Double = 0
|
|
13
|
+
|
|
14
|
+
static func start(intervalSeconds: TimeInterval = 30) {
|
|
15
|
+
stop()
|
|
16
|
+
startMemoryMB = currentMemoryMB()
|
|
17
|
+
Bridge.log("📊 Memory Monitor started - baseline: \(String(format: "%.1f", startMemoryMB)) MB")
|
|
18
|
+
|
|
19
|
+
DispatchQueue.main.async {
|
|
20
|
+
timer = Timer.scheduledTimer(withTimeInterval: intervalSeconds, repeats: true) { _ in
|
|
21
|
+
let current = currentMemoryMB()
|
|
22
|
+
let delta = current - startMemoryMB
|
|
23
|
+
let trend = delta > 50 ? "🔴" : delta > 20 ? "🟡" : "⚪"
|
|
24
|
+
Bridge.log("📊 Memory: \(String(format: "%.1f", current)) MB (Δ \(String(format: "%+.1f", delta)) MB) \(trend)")
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static func stop() {
|
|
30
|
+
timer?.invalidate()
|
|
31
|
+
timer = nil
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static func currentMemoryMB() -> Double {
|
|
35
|
+
var info = mach_task_basic_info()
|
|
36
|
+
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size) / 4
|
|
37
|
+
let result = withUnsafeMutablePointer(to: &info) {
|
|
38
|
+
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
|
|
39
|
+
task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result == KERN_SUCCESS ? Double(info.resident_size) / 1024 / 1024 : 0
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
final class MessageChunkReassembler {
|
|
4
|
+
private static let chunkTimeoutMs: TimeInterval = 30_000
|
|
5
|
+
private static let maxConcurrentSessions = 10
|
|
6
|
+
|
|
7
|
+
private var activeSessions: [String: ChunkSession] = [:]
|
|
8
|
+
|
|
9
|
+
private var activeBinarySessions: [UInt16: BinarySession] = [:]
|
|
10
|
+
|
|
11
|
+
func addBinaryFragment(msgId: UInt16, fragIdx: Int, fragCount: Int, data: Data) -> Data? {
|
|
12
|
+
cleanupTimedOutSessions()
|
|
13
|
+
|
|
14
|
+
guard fragCount > 0, fragIdx >= 0, fragIdx < fragCount else {
|
|
15
|
+
print("MessageChunkReassembler: Dropping invalid binary fragment for msgId \(msgId)")
|
|
16
|
+
return nil
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if let existing = activeBinarySessions[msgId], existing.fragCount != fragCount {
|
|
20
|
+
activeBinarySessions.removeValue(forKey: msgId)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if activeBinarySessions.count >= Self.maxConcurrentSessions,
|
|
24
|
+
activeBinarySessions[msgId] == nil
|
|
25
|
+
{
|
|
26
|
+
removeOldestBinarySession()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let isNewSession = activeBinarySessions[msgId] == nil
|
|
30
|
+
let session = activeBinarySessions[msgId] ?? BinarySession(msgId: msgId, fragCount: fragCount)
|
|
31
|
+
|
|
32
|
+
guard session.addFragment(index: fragIdx, data: data) else {
|
|
33
|
+
return nil
|
|
34
|
+
}
|
|
35
|
+
if isNewSession {
|
|
36
|
+
activeBinarySessions[msgId] = session
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
guard session.isComplete else {
|
|
40
|
+
return nil
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let reassembled = session.reassemble()
|
|
44
|
+
activeBinarySessions.removeValue(forKey: msgId)
|
|
45
|
+
print("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(fragCount) binary fragments")
|
|
46
|
+
return reassembled
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func addChunk(_ info: MessageChunker.ChunkInfo) -> String? {
|
|
50
|
+
cleanupTimedOutSessions()
|
|
51
|
+
|
|
52
|
+
guard !info.chunkId.isEmpty,
|
|
53
|
+
info.totalChunks > 0,
|
|
54
|
+
info.chunkIndex >= 0,
|
|
55
|
+
info.chunkIndex < info.totalChunks
|
|
56
|
+
else {
|
|
57
|
+
print("MessageChunkReassembler: Dropping invalid chunk metadata for \(info.chunkId)")
|
|
58
|
+
return nil
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if let existing = activeSessions[info.chunkId], existing.totalChunks != info.totalChunks {
|
|
62
|
+
print(
|
|
63
|
+
"MessageChunkReassembler: totalChunks mismatch for \(info.chunkId) (expected \(existing.totalChunks), got \(info.totalChunks)); resetting session"
|
|
64
|
+
)
|
|
65
|
+
activeSessions.removeValue(forKey: info.chunkId)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if activeSessions.count >= Self.maxConcurrentSessions,
|
|
69
|
+
activeSessions[info.chunkId] == nil
|
|
70
|
+
{
|
|
71
|
+
removeOldestSession()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let isNewSession = activeSessions[info.chunkId] == nil
|
|
75
|
+
let session = activeSessions[info.chunkId] ?? ChunkSession(
|
|
76
|
+
chunkId: info.chunkId,
|
|
77
|
+
totalChunks: info.totalChunks
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
guard session.addChunk(index: info.chunkIndex, data: info.data) else {
|
|
81
|
+
print("MessageChunkReassembler: Failed to add chunk \(info.chunkIndex) for \(info.chunkId)")
|
|
82
|
+
return nil
|
|
83
|
+
}
|
|
84
|
+
if isNewSession {
|
|
85
|
+
activeSessions[info.chunkId] = session
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
guard session.isComplete else {
|
|
89
|
+
return nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let reassembled = session.reassemble()
|
|
93
|
+
activeSessions.removeValue(forKey: info.chunkId)
|
|
94
|
+
print("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(info.totalChunks) chunks")
|
|
95
|
+
return reassembled
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
func clear() {
|
|
99
|
+
activeSessions.removeAll()
|
|
100
|
+
activeBinarySessions.removeAll()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private func cleanupTimedOutSessions() {
|
|
104
|
+
let now = Date().timeIntervalSince1970 * 1000
|
|
105
|
+
activeSessions = activeSessions.filter { entry in
|
|
106
|
+
now - entry.value.createdAtMs <= Self.chunkTimeoutMs
|
|
107
|
+
}
|
|
108
|
+
activeBinarySessions = activeBinarySessions.filter { entry in
|
|
109
|
+
now - entry.value.createdAtMs <= Self.chunkTimeoutMs
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private func removeOldestBinarySession() {
|
|
114
|
+
guard let oldest = activeBinarySessions.min(by: { $0.value.createdAtMs < $1.value.createdAtMs }) else {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
activeBinarySessions.removeValue(forKey: oldest.key)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private func removeOldestSession() {
|
|
121
|
+
guard let oldest = activeSessions.min(by: { $0.value.createdAtMs < $1.value.createdAtMs }) else {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
activeSessions.removeValue(forKey: oldest.key)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private final class ChunkSession {
|
|
128
|
+
let chunkId: String
|
|
129
|
+
let totalChunks: Int
|
|
130
|
+
let createdAtMs: TimeInterval
|
|
131
|
+
private var chunks: [Int: String] = [:]
|
|
132
|
+
|
|
133
|
+
init(chunkId: String, totalChunks: Int) {
|
|
134
|
+
self.chunkId = chunkId
|
|
135
|
+
self.totalChunks = totalChunks
|
|
136
|
+
self.createdAtMs = Date().timeIntervalSince1970 * 1000
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func addChunk(index: Int, data: String) -> Bool {
|
|
140
|
+
guard index >= 0, index < totalChunks else {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
chunks[index] = data
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
var isComplete: Bool {
|
|
148
|
+
chunks.count == totalChunks
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
func reassemble() -> String {
|
|
152
|
+
var result = ""
|
|
153
|
+
for index in 0 ..< totalChunks {
|
|
154
|
+
result += chunks[index] ?? ""
|
|
155
|
+
}
|
|
156
|
+
return result
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private final class BinarySession {
|
|
161
|
+
let msgId: UInt16
|
|
162
|
+
let fragCount: Int
|
|
163
|
+
let createdAtMs: TimeInterval
|
|
164
|
+
private var fragments: [Int: Data] = [:]
|
|
165
|
+
|
|
166
|
+
init(msgId: UInt16, fragCount: Int) {
|
|
167
|
+
self.msgId = msgId
|
|
168
|
+
self.fragCount = fragCount
|
|
169
|
+
self.createdAtMs = Date().timeIntervalSince1970 * 1000
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
func addFragment(index: Int, data: Data) -> Bool {
|
|
173
|
+
guard index >= 0, index < fragCount else { return false }
|
|
174
|
+
fragments[index] = data
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
var isComplete: Bool {
|
|
179
|
+
fragments.count == fragCount
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func reassemble() -> Data {
|
|
183
|
+
var result = Data()
|
|
184
|
+
for index in 0 ..< fragCount {
|
|
185
|
+
result.append(fragments[index] ?? Data())
|
|
186
|
+
}
|
|
187
|
+
return result
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|